Fixed namespaces (PHPCI -> PHPCensor)
This commit is contained in:
parent
60d74b0b44
commit
60a2b7282a
238 changed files with 1014 additions and 863 deletions
91
src/PHPCensor/Plugin/Atoum.php
Normal file
91
src/PHPCensor/Plugin/Atoum.php
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Atoum plugin, runs Atoum tests within a project.
|
||||
* @package PHPCI\Plugin
|
||||
*/
|
||||
class Atoum implements Plugin
|
||||
{
|
||||
private $args;
|
||||
private $config;
|
||||
private $directory;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
if (isset($options['executable'])) {
|
||||
$this->executable = $this->phpci->buildPath . DIRECTORY_SEPARATOR.$options['executable'];
|
||||
} else {
|
||||
$this->executable = $this->phpci->findBinary('atoum');
|
||||
}
|
||||
|
||||
if (isset($options['args'])) {
|
||||
$this->args = $options['args'];
|
||||
}
|
||||
|
||||
if (isset($options['config'])) {
|
||||
$this->config = $options['config'];
|
||||
}
|
||||
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory = $options['directory'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Atoum plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$cmd = $this->executable;
|
||||
|
||||
if ($this->args !== null) {
|
||||
$cmd .= " {$this->args}";
|
||||
}
|
||||
if ($this->config !== null) {
|
||||
$cmd .= " -c '{$this->config}'";
|
||||
}
|
||||
if ($this->directory !== null) {
|
||||
$dirPath = $this->phpci->buildPath . DIRECTORY_SEPARATOR . $this->directory;
|
||||
$cmd .= " -d '{$dirPath}'";
|
||||
}
|
||||
chdir($this->phpci->buildPath);
|
||||
$output = '';
|
||||
$status = true;
|
||||
exec($cmd, $output);
|
||||
|
||||
if (count(preg_grep("/Success \(/", $output)) == 0) {
|
||||
$status = false;
|
||||
$this->phpci->log($output);
|
||||
}
|
||||
if (count($output) == 0) {
|
||||
$status = false;
|
||||
$this->phpci->log(Lang::get('no_tests_performed'));
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
141
src/PHPCensor/Plugin/Behat.php
Normal file
141
src/PHPCensor/Plugin/Behat.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Model\BuildError;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Behat BDD Plugin
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Behat implements Plugin
|
||||
{
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $features;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->features = '';
|
||||
|
||||
if (isset($options['executable'])) {
|
||||
$this->executable = $options['executable'];
|
||||
} else {
|
||||
$this->executable = $this->phpci->findBinary('behat');
|
||||
}
|
||||
|
||||
if (!empty($options['features'])) {
|
||||
$this->features = $options['features'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs Behat tests.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$curdir = getcwd();
|
||||
chdir($this->phpci->buildPath);
|
||||
|
||||
$behat = $this->executable;
|
||||
|
||||
if (!$behat) {
|
||||
$this->phpci->logFailure(Lang::get('could_not_find', 'behat'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = $this->phpci->executeCommand($behat . ' %s', $this->features);
|
||||
chdir($curdir);
|
||||
|
||||
list($errorCount, $data) = $this->parseBehatOutput();
|
||||
|
||||
$this->build->storeMeta('behat-warnings', $errorCount);
|
||||
$this->build->storeMeta('behat-data', $data);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the behat output and return details on failures
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseBehatOutput()
|
||||
{
|
||||
$output = $this->phpci->getLastOutput();
|
||||
|
||||
$parts = explode('---', $output);
|
||||
|
||||
if (count($parts) <= 1) {
|
||||
return [0, []];
|
||||
}
|
||||
|
||||
$lines = explode(PHP_EOL, $parts[1]);
|
||||
|
||||
$storeFailures = false;
|
||||
$data = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if ($line == 'Failed scenarios:') {
|
||||
$storeFailures = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($line, ':') === false) {
|
||||
$storeFailures = false;
|
||||
}
|
||||
|
||||
if ($storeFailures) {
|
||||
$lineParts = explode(':', $line);
|
||||
$data[] = [
|
||||
'file' => $lineParts[0],
|
||||
'line' => $lineParts[1]
|
||||
];
|
||||
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'behat',
|
||||
'Behat scenario failed.',
|
||||
BuildError::SEVERITY_HIGH,
|
||||
$lineParts[0],
|
||||
$lineParts[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$errorCount = count($data);
|
||||
|
||||
return [$errorCount, $data];
|
||||
}
|
||||
}
|
||||
156
src/PHPCensor/Plugin/Campfire.php
Normal file
156
src/PHPCensor/Plugin/Campfire.php
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Campfire Plugin - Allows Campfire API actions.
|
||||
* strongly based on icecube (http://labs.mimmin.com/icecube)
|
||||
* @author André Cianfarani <acianfa@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Campfire implements Plugin
|
||||
{
|
||||
private $url;
|
||||
private $authToken;
|
||||
private $userAgent;
|
||||
private $cookie;
|
||||
private $verbose;
|
||||
private $roomId;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->message = $options['message'];
|
||||
$this->userAgent = "PHP Censor/1.0";
|
||||
$this->cookie = "php-censor-cookie";
|
||||
|
||||
$buildSettings = $phpci->getConfig('build_settings');
|
||||
|
||||
if (isset($buildSettings['campfire'])) {
|
||||
$campfire = $buildSettings['campfire'];
|
||||
$this->url = $campfire['url'];
|
||||
$this->authToken = $campfire['authToken'];
|
||||
$this->roomId = $campfire['roomId'];
|
||||
} else {
|
||||
throw new \Exception(Lang::get('no_campfire_settings'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Campfire plugin.
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$url = PHPCI_URL . "build/view/" . $this->build->getId();
|
||||
$message = str_replace("%buildurl%", $url, $this->message);
|
||||
$this->joinRoom($this->roomId);
|
||||
$status = $this->speak($message, $this->roomId);
|
||||
$this->leaveRoom($this->roomId);
|
||||
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a Campfire room.
|
||||
* @param $roomId
|
||||
*/
|
||||
public function joinRoom($roomId)
|
||||
{
|
||||
$this->getPageByPost('/room/'.$roomId.'/join.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave a Campfire room.
|
||||
* @param $roomId
|
||||
*/
|
||||
public function leaveRoom($roomId)
|
||||
{
|
||||
$this->getPageByPost('/room/'.$roomId.'/leave.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a campfire room.
|
||||
* @param $message
|
||||
* @param $roomId
|
||||
* @param bool $isPaste
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function speak($message, $roomId, $isPaste = false)
|
||||
{
|
||||
$page = '/room/'.$roomId.'/speak.json';
|
||||
|
||||
if ($isPaste) {
|
||||
$type = 'PasteMessage';
|
||||
} else {
|
||||
$type = 'TextMessage';
|
||||
}
|
||||
|
||||
return $this->getPageByPost($page, ['message' => ['type' => $type, 'body' => $message]]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to Campfire.
|
||||
* @param $page
|
||||
* @param null $data
|
||||
* @return bool|mixed
|
||||
*/
|
||||
private function getPageByPost($page, $data = null)
|
||||
{
|
||||
$url = $this->url . $page;
|
||||
// The new API allows JSON, so we can pass
|
||||
// PHP data structures instead of old school POST
|
||||
$json = json_encode($data);
|
||||
|
||||
// cURL init & config
|
||||
$handle = curl_init();
|
||||
curl_setopt($handle, CURLOPT_URL, $url);
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($handle, CURLOPT_POST, 1);
|
||||
curl_setopt($handle, CURLOPT_USERAGENT, $this->userAgent);
|
||||
curl_setopt($handle, CURLOPT_VERBOSE, $this->verbose);
|
||||
curl_setopt($handle, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($handle, CURLOPT_USERPWD, $this->authToken . ':x');
|
||||
curl_setopt($handle, CURLOPT_HTTPHEADER, ["Content-type: application/json"]);
|
||||
curl_setopt($handle, CURLOPT_COOKIEFILE, $this->cookie);
|
||||
|
||||
curl_setopt($handle, CURLOPT_POSTFIELDS, $json);
|
||||
$output = curl_exec($handle);
|
||||
|
||||
curl_close($handle);
|
||||
|
||||
// We tend to get one space with an otherwise blank response
|
||||
$output = trim($output);
|
||||
|
||||
if (strlen($output)) {
|
||||
/* Responses are JSON. Decode it to a data structure */
|
||||
return json_decode($output);
|
||||
}
|
||||
|
||||
// Simple 200 OK response (such as for joining a room)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
74
src/PHPCensor/Plugin/CleanBuild.php
Normal file
74
src/PHPCensor/Plugin/CleanBuild.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Clean build removes Composer related files and allows PHPCI users to clean up their build directory.
|
||||
* Useful as a precursor to copy_build.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class CleanBuild implements Plugin
|
||||
{
|
||||
protected $remove;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : [];
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Composer and runs a specified command (e.g. install / update)
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$cmd = 'rm -Rf "%s"';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'rmdir /S /Q "%s"';
|
||||
}
|
||||
$this->phpci->executeCommand($cmd, $this->phpci->buildPath . 'composer.phar');
|
||||
$this->phpci->executeCommand($cmd, $this->phpci->buildPath . 'composer.lock');
|
||||
|
||||
$success = true;
|
||||
|
||||
foreach ($this->remove as $file) {
|
||||
$ok = $this->phpci->executeCommand($cmd, $this->phpci->buildPath . $file);
|
||||
|
||||
if (!$ok) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
167
src/PHPCensor/Plugin/Codeception.php
Normal file
167
src/PHPCensor/Plugin/Codeception.php
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin\Util\TestResultParsers\Codeception as Parser;
|
||||
use Psr\Log\LogLevel;
|
||||
use PHPCensor\Plugin;
|
||||
use PHPCensor\ZeroConfigPlugin;
|
||||
|
||||
/**
|
||||
* Codeception Plugin - Enables full acceptance, unit, and functional testing.
|
||||
* @author Don Gilbert <don@dongilbert.net>
|
||||
* @author Igor Timoshenko <contact@igortimoshenko.com>
|
||||
* @author Adam Cooper <adam@networkpie.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Codeception implements Plugin, ZeroConfigPlugin
|
||||
{
|
||||
/** @var string */
|
||||
protected $args = '';
|
||||
|
||||
/** @var Builder */
|
||||
protected $phpci;
|
||||
|
||||
/** @var Build */
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string $ymlConfigFile The path of a yml config for Codeception
|
||||
*/
|
||||
protected $ymlConfigFile;
|
||||
|
||||
/**
|
||||
* @var string $path The path to the codeception tests folder.
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
return $stage == 'test' && !is_null(self::findConfigFile($builder->buildPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try and find the codeception YML config file.
|
||||
* @param $buildPath
|
||||
* @return null|string
|
||||
*/
|
||||
public static function findConfigFile($buildPath)
|
||||
{
|
||||
if (file_exists($buildPath . 'codeception.yml')) {
|
||||
return 'codeception.yml';
|
||||
}
|
||||
|
||||
if (file_exists($buildPath . 'codeception.dist.yml')) {
|
||||
return 'codeception.dist.yml';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->path = 'tests' . DIRECTORY_SEPARATOR . '_output' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if (empty($options['config'])) {
|
||||
$this->ymlConfigFile = self::findConfigFile($this->phpci->buildPath);
|
||||
} else {
|
||||
$this->ymlConfigFile = $options['config'];
|
||||
}
|
||||
if (isset($options['args'])) {
|
||||
$this->args = (string) $options['args'];
|
||||
}
|
||||
if (isset($options['path'])) {
|
||||
$this->path = $options['path'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs Codeception tests
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (empty($this->ymlConfigFile)) {
|
||||
throw new \Exception("No configuration file found");
|
||||
}
|
||||
|
||||
// Run any config files first. This can be either a single value or an array.
|
||||
return $this->runConfigFile($this->ymlConfigFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run tests from a Codeception config file.
|
||||
* @param $configPath
|
||||
* @return bool|mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function runConfigFile($configPath)
|
||||
{
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
$codecept = $this->phpci->findBinary('codecept');
|
||||
|
||||
if (!$codecept) {
|
||||
$this->phpci->logFailure(Lang::get('could_not_find', 'codecept'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
|
||||
|
||||
if (IS_WIN) {
|
||||
$cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
|
||||
}
|
||||
|
||||
$configPath = $this->phpci->buildPath . $configPath;
|
||||
$success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath);
|
||||
|
||||
$this->phpci->log(
|
||||
'Codeception XML path: '. $this->phpci->buildPath . $this->path . 'report.xml',
|
||||
Loglevel::DEBUG
|
||||
);
|
||||
|
||||
$xml = file_get_contents($this->phpci->buildPath . $this->path . 'report.xml', false);
|
||||
$parser = new Parser($this->phpci, $xml);
|
||||
$output = $parser->parse();
|
||||
|
||||
$meta = [
|
||||
'tests' => $parser->getTotalTests(),
|
||||
'timetaken' => $parser->getTotalTimeTaken(),
|
||||
'failures' => $parser->getTotalFailures()
|
||||
];
|
||||
|
||||
$this->build->storeMeta('codeception-meta', $meta);
|
||||
$this->build->storeMeta('codeception-data', $output);
|
||||
$this->build->storeMeta('codeception-errors', $parser->getTotalFailures());
|
||||
$this->phpci->logExecOutput(true);
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
136
src/PHPCensor/Plugin/Composer.php
Normal file
136
src/PHPCensor/Plugin/Composer.php
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* Composer Plugin - Provides access to Composer functionality.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Composer implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $action;
|
||||
protected $preferDist;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $nodev;
|
||||
protected $ignorePlatformReqs;
|
||||
protected $preferSource;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
$path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.json';
|
||||
|
||||
if (file_exists($path) && $stage == 'setup') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directory = $path;
|
||||
$this->action = 'install';
|
||||
$this->preferDist = false;
|
||||
$this->preferSource = false;
|
||||
$this->nodev = false;
|
||||
$this->ignorePlatformReqs = false;
|
||||
|
||||
if (array_key_exists('directory', $options)) {
|
||||
$this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
|
||||
}
|
||||
|
||||
if (array_key_exists('action', $options)) {
|
||||
$this->action = $options['action'];
|
||||
}
|
||||
|
||||
if (array_key_exists('prefer_dist', $options)) {
|
||||
$this->preferDist = (bool)$options['prefer_dist'];
|
||||
}
|
||||
|
||||
if (array_key_exists('prefer_source', $options)) {
|
||||
$this->preferDist = false;
|
||||
$this->preferSource = (bool)$options['prefer_source'];
|
||||
}
|
||||
|
||||
if (array_key_exists('no_dev', $options)) {
|
||||
$this->nodev = (bool)$options['no_dev'];
|
||||
}
|
||||
|
||||
if (array_key_exists('ignore_platform_reqs', $options)) {
|
||||
$this->ignorePlatformReqs = (bool)$options['ignore_platform_reqs'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Composer and runs a specified command (e.g. install / update)
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$composerLocation = $this->phpci->findBinary(['composer', 'composer.phar']);
|
||||
|
||||
$cmd = '';
|
||||
|
||||
if (IS_WIN) {
|
||||
$cmd = 'php ';
|
||||
}
|
||||
|
||||
$cmd .= $composerLocation . ' --no-ansi --no-interaction ';
|
||||
|
||||
if ($this->preferDist) {
|
||||
$this->phpci->log('Using --prefer-dist flag');
|
||||
$cmd .= ' --prefer-dist';
|
||||
}
|
||||
|
||||
if ($this->preferSource) {
|
||||
$this->phpci->log('Using --prefer-source flag');
|
||||
$cmd .= ' --prefer-source';
|
||||
}
|
||||
|
||||
if ($this->nodev) {
|
||||
$this->phpci->log('Using --no-dev flag');
|
||||
$cmd .= ' --no-dev';
|
||||
}
|
||||
|
||||
if ($this->ignorePlatformReqs) {
|
||||
$this->phpci->log('Using --ignore-platform-reqs flag');
|
||||
$cmd .= ' --ignore-platform-reqs';
|
||||
}
|
||||
|
||||
$cmd .= ' --working-dir="%s" %s';
|
||||
|
||||
return $this->phpci->executeCommand($cmd, $this->directory, $this->action);
|
||||
}
|
||||
}
|
||||
105
src/PHPCensor/Plugin/CopyBuild.php
Normal file
105
src/PHPCensor/Plugin/CopyBuild.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Copy Build Plugin - Copies the entire build to another directory.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class CopyBuild implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $ignore;
|
||||
protected $wipe;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directory = isset($options['directory']) ? $options['directory'] : $path;
|
||||
$this->wipe = isset($options['wipe']) ? (bool)$options['wipe'] : false;
|
||||
$this->ignore = isset($options['respect_ignore']) ? (bool)$options['respect_ignore'] : false;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies files from the root of the build directory into the target folder
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$build = $this->phpci->buildPath;
|
||||
|
||||
if ($this->directory == $build) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->wipeExistingDirectory();
|
||||
|
||||
$cmd = 'mkdir -p "%s" && cp -R "%s" "%s"';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'mkdir -p "%s" && xcopy /E "%s" "%s"';
|
||||
}
|
||||
|
||||
$success = $this->phpci->executeCommand($cmd, $this->directory, $build, $this->directory);
|
||||
|
||||
$this->deleteIgnoredFiles();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipe the destination directory if it already exists.
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function wipeExistingDirectory()
|
||||
{
|
||||
if ($this->wipe === true && $this->directory != '/' && is_dir($this->directory)) {
|
||||
$cmd = 'rm -Rf "%s*"';
|
||||
$success = $this->phpci->executeCommand($cmd, $this->directory);
|
||||
|
||||
if (!$success) {
|
||||
throw new \Exception(Lang::get('failed_to_wipe', $this->directory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete any ignored files from the build prior to copying.
|
||||
*/
|
||||
protected function deleteIgnoredFiles()
|
||||
{
|
||||
if ($this->ignore) {
|
||||
foreach ($this->phpci->ignore as $file) {
|
||||
$cmd = 'rm -Rf "%s/%s"';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'rmdir /S /Q "%s\%s"';
|
||||
}
|
||||
$this->phpci->executeCommand($cmd, $this->directory, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/PHPCensor/Plugin/Deployer.php
Normal file
74
src/PHPCensor/Plugin/Deployer.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2015, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use b8\HttpClient;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Integrates PHPCI with Deployer: https://github.com/rebelinblue/deployer
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Deployer implements Plugin
|
||||
{
|
||||
protected $webhookUrl;
|
||||
protected $reason;
|
||||
protected $updateOnly;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->reason = 'PHP Censor Build #%BUILD% - %COMMIT_MESSAGE%';
|
||||
|
||||
if (isset($options['webhook_url'])) {
|
||||
$this->webhookUrl = $options['webhook_url'];
|
||||
}
|
||||
|
||||
if (isset($options['reason'])) {
|
||||
$this->reason = $options['reason'];
|
||||
}
|
||||
|
||||
$this->updateOnly = isset($options['update_only']) ? (bool) $options['update_only'] : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies files from the root of the build directory into the target folder
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (empty($this->webhookUrl)) {
|
||||
$this->phpci->logFailure('You must specify a webhook URL.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$http = new HttpClient();
|
||||
|
||||
$response = $http->post($this->webhookUrl, [
|
||||
'reason' => $this->phpci->interpolate($this->reason),
|
||||
'source' => 'PHP Censor',
|
||||
'url' => $this->phpci->interpolate('%BUILD_URI%'),
|
||||
'branch' => $this->phpci->interpolate('%BRANCH%'),
|
||||
'update_only' => $this->updateOnly
|
||||
]);
|
||||
|
||||
return $response['success'];
|
||||
}
|
||||
}
|
||||
245
src/PHPCensor/Plugin/Email.php
Normal file
245
src/PHPCensor/Plugin/Email.php
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use Exception;
|
||||
use b8\View;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Helper\Email as EmailHelper;
|
||||
use Psr\Log\LogLevel;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Email Plugin - Provides simple email capability to PHPCI.
|
||||
* @author Steve Brazier <meadsteve@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Email implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->options = $options;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification mail.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$addresses = $this->getEmailAddresses();
|
||||
|
||||
// Without some email addresses in the yml file then we
|
||||
// can't do anything.
|
||||
if (count($addresses) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$buildStatus = $this->build->isSuccessful() ? "Passing Build" : "Failing Build";
|
||||
$projectName = $this->build->getProject()->getTitle();
|
||||
|
||||
try {
|
||||
$view = $this->getMailTemplate();
|
||||
} catch (Exception $e) {
|
||||
$this->phpci->log(
|
||||
sprintf('Unknown mail template "%s", falling back to default.', $this->options['template']),
|
||||
LogLevel::WARNING
|
||||
);
|
||||
$view = $this->getDefaultMailTemplate();
|
||||
}
|
||||
|
||||
$view->build = $this->build;
|
||||
$view->project = $this->build->getProject();
|
||||
|
||||
$layout = new View('Email/layout');
|
||||
$layout->build = $this->build;
|
||||
$layout->project = $this->build->getProject();
|
||||
$layout->content = $view->render();
|
||||
$body = $layout->render();
|
||||
|
||||
$sendFailures = $this->sendSeparateEmails(
|
||||
$addresses,
|
||||
sprintf("PHP Censor - %s - %s", $projectName, $buildStatus),
|
||||
$body
|
||||
);
|
||||
|
||||
// This is a success if we've not failed to send anything.
|
||||
$this->phpci->log(Lang::get('n_emails_sent', (count($addresses) - $sendFailures)));
|
||||
$this->phpci->log(Lang::get('n_emails_failed', $sendFailures));
|
||||
|
||||
return ($sendFailures === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $toAddress Single address to send to
|
||||
* @param string[] $ccList
|
||||
* @param string $subject Email subject
|
||||
* @param string $body Email body
|
||||
* @return array Array of failed addresses
|
||||
*/
|
||||
protected function sendEmail($toAddress, $ccList, $subject, $body)
|
||||
{
|
||||
$email = new EmailHelper();
|
||||
|
||||
$email->setEmailTo($toAddress, $toAddress);
|
||||
$email->setSubject($subject);
|
||||
$email->setBody($body);
|
||||
$email->setHtml(true);
|
||||
|
||||
if (is_array($ccList) && count($ccList)) {
|
||||
foreach ($ccList as $address) {
|
||||
$email->addCc($address, $address);
|
||||
}
|
||||
}
|
||||
|
||||
return $email->send($this->phpci);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email to a list of specified subjects.
|
||||
*
|
||||
* @param array $toAddresses
|
||||
* List of destination addresses for message.
|
||||
* @param string $subject
|
||||
* Mail subject
|
||||
* @param string $body
|
||||
* Mail body
|
||||
*
|
||||
* @return int number of failed messages
|
||||
*/
|
||||
public function sendSeparateEmails(array $toAddresses, $subject, $body)
|
||||
{
|
||||
$failures = 0;
|
||||
$ccList = $this->getCcAddresses();
|
||||
|
||||
foreach ($toAddresses as $address) {
|
||||
if (!$this->sendEmail($address, $ccList, $subject, $body)) {
|
||||
$failures++;
|
||||
}
|
||||
}
|
||||
|
||||
return $failures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of email addresses to send to.
|
||||
* @return array
|
||||
*/
|
||||
protected function getEmailAddresses()
|
||||
{
|
||||
$addresses = [];
|
||||
$committer = $this->build->getCommitterEmail();
|
||||
|
||||
$this->phpci->logDebug(sprintf("Committer email: '%s'", $committer));
|
||||
$this->phpci->logDebug(sprintf(
|
||||
"Committer option: '%s'",
|
||||
(!empty($this->options['committer']) && $this->options['committer']) ? 'true' : 'false'
|
||||
));
|
||||
|
||||
if (!empty($this->options['committer']) && $this->options['committer']) {
|
||||
if ($committer) {
|
||||
$addresses[] = $committer;
|
||||
}
|
||||
}
|
||||
|
||||
$this->phpci->logDebug(sprintf(
|
||||
"Addresses option: '%s'",
|
||||
(!empty($this->options['addresses']) && is_array($this->options['addresses'])) ? implode(', ', $this->options['addresses']) : 'false'
|
||||
));
|
||||
|
||||
if (!empty($this->options['addresses']) && is_array($this->options['addresses'])) {
|
||||
foreach ($this->options['addresses'] as $address) {
|
||||
$addresses[] = $address;
|
||||
}
|
||||
}
|
||||
|
||||
$this->phpci->logDebug(sprintf(
|
||||
"Default mailTo option: '%s'",
|
||||
!empty($this->options['default_mailto_address']) ? $this->options['default_mailto_address'] : 'false'
|
||||
));
|
||||
|
||||
if (empty($addresses) && !empty($this->options['default_mailto_address'])) {
|
||||
$addresses[] = $this->options['default_mailto_address'];
|
||||
}
|
||||
|
||||
return array_unique($addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of email addresses to CC.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getCcAddresses()
|
||||
{
|
||||
$ccAddresses = [];
|
||||
|
||||
if (isset($this->options['cc'])) {
|
||||
foreach ($this->options['cc'] as $address) {
|
||||
$ccAddresses[] = $address;
|
||||
}
|
||||
}
|
||||
|
||||
return $ccAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail template used to sent the mail.
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
protected function getMailTemplate()
|
||||
{
|
||||
if (isset($this->options['template'])) {
|
||||
return new View('Email/' . $this->options['template']);
|
||||
}
|
||||
|
||||
return $this->getDefaultMailTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default mail template.
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
protected function getDefaultMailTemplate()
|
||||
{
|
||||
$template = $this->build->isSuccessful() ? 'short' : 'long';
|
||||
|
||||
return new View('Email/' . $template);
|
||||
}
|
||||
}
|
||||
66
src/PHPCensor/Plugin/Env.php
Normal file
66
src/PHPCensor/Plugin/Env.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Environment variable plugin
|
||||
* @author Steve Kamerman <stevekamerman@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Env implements Plugin
|
||||
{
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $env_vars;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->env_vars = $options;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified environment variables to the builder environment
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$success = true;
|
||||
foreach ($this->env_vars as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
// This allows the developer to specify env vars like " - FOO=bar" or " - FOO: bar"
|
||||
$env_var = is_array($value)? key($value).'='.current($value): $value;
|
||||
} else {
|
||||
// This allows the standard syntax: "FOO: bar"
|
||||
$env_var = "$key=$value";
|
||||
}
|
||||
|
||||
if (!putenv($this->phpci->interpolate($env_var))) {
|
||||
$success = false;
|
||||
$this->phpci->logFailure(Lang::get('unable_to_set_env'));
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
74
src/PHPCensor/Plugin/FlowdockNotify.php
Normal file
74
src/PHPCensor/Plugin/FlowdockNotify.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use Mremi\Flowdock\Api\Push\Push;
|
||||
use Mremi\Flowdock\Api\Push\TeamInboxMessage;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Flowdock Plugin
|
||||
* @author Petr Cervenka <petr@nanosolutions.io>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class FlowdockNotify implements Plugin
|
||||
{
|
||||
private $api_key;
|
||||
private $email;
|
||||
const MESSAGE_DEFAULT = 'Build %BUILD% has finished for commit <a href="%COMMIT_URI%">%SHORT_COMMIT%</a>
|
||||
(%COMMIT_EMAIL%)> on branch <a href="%BRANCH_URI%">%BRANCH%</a>';
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
if (!is_array($options) || !isset($options['api_key'])) {
|
||||
throw new \Exception('Please define the api_key for Flowdock Notify plugin!');
|
||||
}
|
||||
$this->api_key = trim($options['api_key']);
|
||||
$this->message = isset($options['message']) ? $options['message'] : self::MESSAGE_DEFAULT;
|
||||
$this->email = isset($options['email']) ? $options['email'] : 'PHP Censor';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Flowdock plugin.
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
|
||||
$message = $this->phpci->interpolate($this->message);
|
||||
$successfulBuild = $this->build->isSuccessful() ? 'Success' : 'Failed';
|
||||
$push = new Push($this->api_key);
|
||||
$flowMessage = TeamInboxMessage::create()
|
||||
->setSource("PHPCensor")
|
||||
->setFromAddress($this->email)
|
||||
->setFromName($this->build->getProject()->getTitle())
|
||||
->setSubject($successfulBuild)
|
||||
->setTags(['#ci'])
|
||||
->setLink($this->build->getBranchLink())
|
||||
->setContent($message);
|
||||
|
||||
if (!$push->sendTeamInboxMessage($flowMessage, ['connect_timeout' => 5000, 'timeout' => 5000])) {
|
||||
throw new \Exception(sprintf('Flowdock Failed: %s', $flowMessage->getResponseErrors()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
177
src/PHPCensor/Plugin/Git.php
Normal file
177
src/PHPCensor/Plugin/Git.php
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Git plugin.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Git implements Plugin
|
||||
{
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $actions = [];
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->actions = $options;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Git plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$buildPath = $this->phpci->buildPath;
|
||||
|
||||
// Check if there are any actions to be run for the branch we're running on:
|
||||
if (!array_key_exists($this->build->getBranch(), $this->actions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there are, run them:
|
||||
$curdir = getcwd();
|
||||
chdir($buildPath);
|
||||
|
||||
$success = true;
|
||||
foreach ($this->actions[$this->build->getBranch()] as $action => $options) {
|
||||
if (!$this->runAction($action, $options)) {
|
||||
$success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chdir($curdir);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which action to run, and run it.
|
||||
* @param $action
|
||||
* @param array $options
|
||||
* @return bool
|
||||
*/
|
||||
protected function runAction($action, array $options = [])
|
||||
{
|
||||
switch ($action) {
|
||||
case 'merge':
|
||||
return $this->runMergeAction($options);
|
||||
|
||||
case 'tag':
|
||||
return $this->runTagAction($options);
|
||||
|
||||
case 'pull':
|
||||
return $this->runPullAction($options);
|
||||
|
||||
case 'push':
|
||||
return $this->runPushAction($options);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a merge action.
|
||||
* @param $options
|
||||
* @return bool
|
||||
*/
|
||||
protected function runMergeAction($options)
|
||||
{
|
||||
if (array_key_exists('branch', $options)) {
|
||||
$cmd = 'cd "%s" && git checkout %s && git merge "%s"';
|
||||
$path = $this->phpci->buildPath;
|
||||
return $this->phpci->executeCommand($cmd, $path, $options['branch'], $this->build->getBranch());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a tag action.
|
||||
* @param $options
|
||||
* @return bool
|
||||
*/
|
||||
protected function runTagAction($options)
|
||||
{
|
||||
$tagName = date('Ymd-His');
|
||||
$message = Lang::get('tag_created', date('Y-m-d H:i:s'));
|
||||
|
||||
if (array_key_exists('name', $options)) {
|
||||
$tagName = $this->phpci->interpolate($options['name']);
|
||||
}
|
||||
|
||||
if (array_key_exists('message', $options)) {
|
||||
$message = $this->phpci->interpolate($options['message']);
|
||||
}
|
||||
|
||||
$cmd = 'git tag %s -m "%s"';
|
||||
return $this->phpci->executeCommand($cmd, $tagName, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a pull action.
|
||||
* @param $options
|
||||
* @return bool
|
||||
*/
|
||||
protected function runPullAction($options)
|
||||
{
|
||||
$branch = $this->build->getBranch();
|
||||
$remote = 'origin';
|
||||
|
||||
if (array_key_exists('branch', $options)) {
|
||||
$branch = $this->phpci->interpolate($options['branch']);
|
||||
}
|
||||
|
||||
if (array_key_exists('remote', $options)) {
|
||||
$remote = $this->phpci->interpolate($options['remote']);
|
||||
}
|
||||
|
||||
return $this->phpci->executeCommand('git pull %s %s', $remote, $branch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a push action.
|
||||
* @param $options
|
||||
* @return bool
|
||||
*/
|
||||
protected function runPushAction($options)
|
||||
{
|
||||
$branch = $this->build->getBranch();
|
||||
$remote = 'origin';
|
||||
|
||||
if (array_key_exists('branch', $options)) {
|
||||
$branch = $this->phpci->interpolate($options['branch']);
|
||||
}
|
||||
|
||||
if (array_key_exists('remote', $options)) {
|
||||
$remote = $this->phpci->interpolate($options['remote']);
|
||||
}
|
||||
|
||||
return $this->phpci->executeCommand('git push %s %s', $remote, $branch);
|
||||
}
|
||||
}
|
||||
100
src/PHPCensor/Plugin/Grunt.php
Normal file
100
src/PHPCensor/Plugin/Grunt.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Grunt Plugin - Provides access to grunt functionality.
|
||||
* @author Tobias Tom <t.tom@succont.de>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Grunt implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $task;
|
||||
protected $preferDist;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $grunt;
|
||||
protected $gruntfile;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->build = $build;
|
||||
$this->phpci = $phpci;
|
||||
$this->directory = $path;
|
||||
$this->task = null;
|
||||
$this->grunt = $this->phpci->findBinary('grunt');
|
||||
$this->gruntfile = 'Gruntfile.js';
|
||||
|
||||
// Handle options:
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
|
||||
}
|
||||
|
||||
if (isset($options['task'])) {
|
||||
$this->task = $options['task'];
|
||||
}
|
||||
|
||||
if (isset($options['grunt'])) {
|
||||
$this->grunt = $options['grunt'];
|
||||
}
|
||||
|
||||
if (isset($options['gruntfile'])) {
|
||||
$this->gruntfile = $options['gruntfile'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes grunt and runs a specified command (e.g. install / update)
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// if npm does not work, we cannot use grunt, so we return false
|
||||
$cmd = 'cd %s && npm install';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'cd /d %s && npm install';
|
||||
}
|
||||
if (!$this->phpci->executeCommand($cmd, $this->directory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the grunt command
|
||||
$cmd = 'cd %s && ' . $this->grunt;
|
||||
if (IS_WIN) {
|
||||
$cmd = 'cd /d %s && ' . $this->grunt;
|
||||
}
|
||||
$cmd .= ' --no-color';
|
||||
$cmd .= ' --gruntfile %s';
|
||||
$cmd .= ' %s'; // the task that will be executed
|
||||
|
||||
// and execute it
|
||||
return $this->phpci->executeCommand($cmd, $this->directory, $this->gruntfile, $this->task);
|
||||
}
|
||||
}
|
||||
100
src/PHPCensor/Plugin/Gulp.php
Normal file
100
src/PHPCensor/Plugin/Gulp.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Gulp Plugin - Provides access to gulp functionality.
|
||||
* @author Dirk Heilig <dirk@heilig-online.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Gulp implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $task;
|
||||
protected $preferDist;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $gulp;
|
||||
protected $gulpfile;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->build = $build;
|
||||
$this->phpci = $phpci;
|
||||
$this->directory = $path;
|
||||
$this->task = null;
|
||||
$this->gulp = $this->phpci->findBinary('gulp');
|
||||
$this->gulpfile = 'gulpfile.js';
|
||||
|
||||
// Handle options:
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory = $path . DIRECTORY_SEPARATOR . $options['directory'];
|
||||
}
|
||||
|
||||
if (isset($options['task'])) {
|
||||
$this->task = $options['task'];
|
||||
}
|
||||
|
||||
if (isset($options['gulp'])) {
|
||||
$this->gulp = $options['gulp'];
|
||||
}
|
||||
|
||||
if (isset($options['gulpfile'])) {
|
||||
$this->gulpfile = $options['gulpfile'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes gulp and runs a specified command (e.g. install / update)
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// if npm does not work, we cannot use gulp, so we return false
|
||||
$cmd = 'cd %s && npm install';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'cd /d %s && npm install';
|
||||
}
|
||||
if (!$this->phpci->executeCommand($cmd, $this->directory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the gulp command
|
||||
$cmd = 'cd %s && ' . $this->gulp;
|
||||
if (IS_WIN) {
|
||||
$cmd = 'cd /d %s && ' . $this->gulp;
|
||||
}
|
||||
$cmd .= ' --no-color';
|
||||
$cmd .= ' --gulpfile %s';
|
||||
$cmd .= ' %s'; // the task that will be executed
|
||||
|
||||
// and execute it
|
||||
return $this->phpci->executeCommand($cmd, $this->directory, $this->gulpfile, $this->task);
|
||||
}
|
||||
}
|
||||
95
src/PHPCensor/Plugin/HipchatNotify.php
Normal file
95
src/PHPCensor/Plugin/HipchatNotify.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
use HipChat\HipChat;
|
||||
|
||||
/**
|
||||
* Hipchat Plugin
|
||||
* @author James Inman <james@jamesinman.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class HipchatNotify implements Plugin
|
||||
{
|
||||
protected $authToken;
|
||||
protected $color;
|
||||
protected $notify;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->userAgent = "PHP Censor/1.0";
|
||||
$this->cookie = "php-censor-cookie";
|
||||
|
||||
if (is_array($options) && isset($options['authToken']) && isset($options['room'])) {
|
||||
$this->authToken = $options['authToken'];
|
||||
$this->room = $options['room'];
|
||||
|
||||
if (isset($options['message'])) {
|
||||
$this->message = $options['message'];
|
||||
} else {
|
||||
$this->message = Lang::get('x_built_at_x');
|
||||
}
|
||||
|
||||
if (isset($options['color'])) {
|
||||
$this->color = $options['color'];
|
||||
} else {
|
||||
$this->color = 'yellow';
|
||||
}
|
||||
|
||||
if (isset($options['notify'])) {
|
||||
$this->notify = $options['notify'];
|
||||
} else {
|
||||
$this->notify = false;
|
||||
}
|
||||
} else {
|
||||
throw new \Exception(Lang::get('hipchat_settings'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the HipChat plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$hipChat = new HipChat($this->authToken);
|
||||
$message = $this->phpci->interpolate($this->message);
|
||||
|
||||
$result = true;
|
||||
if (is_array($this->room)) {
|
||||
foreach ($this->room as $room) {
|
||||
if (!$hipChat->message_room($room, 'PHP Censor', $message, $this->notify, $this->color)) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$hipChat->message_room($this->room, 'PHP Censor', $message, $this->notify, $this->color)) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
135
src/PHPCensor/Plugin/Irc.php
Normal file
135
src/PHPCensor/Plugin/Irc.php
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* IRC Plugin - Sends a notification to an IRC channel
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Irc implements Plugin
|
||||
{
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
protected $message;
|
||||
protected $server;
|
||||
protected $port;
|
||||
protected $room;
|
||||
protected $nick;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->message = $options['message'];
|
||||
|
||||
$buildSettings = $phpci->getConfig('build_settings');
|
||||
|
||||
|
||||
if (isset($buildSettings['irc'])) {
|
||||
$irc = $buildSettings['irc'];
|
||||
|
||||
$this->server = $irc['server'];
|
||||
$this->port = $irc['port'];
|
||||
$this->room = $irc['room'];
|
||||
$this->nick = $irc['nick'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run IRC plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$msg = $this->phpci->interpolate($this->message);
|
||||
|
||||
if (empty($this->server) || empty($this->room) || empty($this->nick)) {
|
||||
$this->phpci->logFailure(Lang::get('irc_settings'));
|
||||
}
|
||||
|
||||
if (empty($this->port)) {
|
||||
$this->port = 6667;
|
||||
}
|
||||
|
||||
$sock = fsockopen($this->server, $this->port);
|
||||
stream_set_timeout($sock, 1);
|
||||
|
||||
$connectCommands = [
|
||||
'USER ' . $this->nick . ' 0 * :' . $this->nick,
|
||||
'NICK ' . $this->nick,
|
||||
];
|
||||
$this->executeIrcCommands($sock, $connectCommands);
|
||||
$this->executeIrcCommand($sock, 'JOIN ' . $this->room);
|
||||
$this->executeIrcCommand($sock, 'PRIVMSG ' . $this->room . ' :' . $msg);
|
||||
|
||||
fclose($sock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $socket
|
||||
* @param array $commands
|
||||
* @return bool
|
||||
*/
|
||||
private function executeIrcCommands($socket, array $commands)
|
||||
{
|
||||
foreach ($commands as $command) {
|
||||
fputs($socket, $command . "\n");
|
||||
}
|
||||
|
||||
$pingBack = false;
|
||||
|
||||
// almost all servers expect pingback!
|
||||
while ($response = fgets($socket)) {
|
||||
$matches = [];
|
||||
if (preg_match('/^PING \\:([A-Z0-9]+)/', $response, $matches)) {
|
||||
$pingBack = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($pingBack) {
|
||||
$command = 'PONG :' . $pingBack . "\n";
|
||||
fputs($socket, $command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource $socket
|
||||
* @param string $command
|
||||
* @return bool
|
||||
*/
|
||||
private function executeIrcCommand($socket, $command)
|
||||
{
|
||||
return $this->executeIrcCommands($socket, [$command]);
|
||||
}
|
||||
}
|
||||
152
src/PHPCensor/Plugin/Lint.php
Normal file
152
src/PHPCensor/Plugin/Lint.php
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHP Lint Plugin - Provides access to PHP lint functionality.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Lint implements PHPCensor\Plugin
|
||||
{
|
||||
protected $directories;
|
||||
protected $recursive = true;
|
||||
protected $ignore;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directories = [''];
|
||||
$this->ignore = $phpci->ignore;
|
||||
|
||||
if (!empty($options['directory'])) {
|
||||
$this->directories[] = $options['directory'];
|
||||
}
|
||||
|
||||
if (!empty($options['directories'])) {
|
||||
$this->directories = $options['directories'];
|
||||
}
|
||||
|
||||
if (array_key_exists('recursive', $options)) {
|
||||
$this->recursive = $options['recursive'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes parallel lint
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$this->phpci->quiet = true;
|
||||
$success = true;
|
||||
|
||||
$php = $this->phpci->findBinary('php');
|
||||
|
||||
foreach ($this->directories as $dir) {
|
||||
if (!$this->lintDirectory($php, $dir)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->phpci->quiet = false;
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lint an item (file or directory) by calling the appropriate method.
|
||||
* @param $php
|
||||
* @param $item
|
||||
* @param $itemPath
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintItem($php, $item, $itemPath)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
if ($item->isFile() && $item->getExtension() == 'php' && !$this->lintFile($php, $itemPath)) {
|
||||
$success = false;
|
||||
} elseif ($item->isDir() && $this->recursive && !$this->lintDirectory($php, $itemPath . DIRECTORY_SEPARATOR)) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run php -l against a directory of files.
|
||||
* @param $php
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintDirectory($php, $path)
|
||||
{
|
||||
$success = true;
|
||||
$directory = new \DirectoryIterator($this->phpci->buildPath . $path);
|
||||
|
||||
foreach ($directory as $item) {
|
||||
if ($item->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemPath = $path . $item->getFilename();
|
||||
|
||||
if (in_array($itemPath, $this->ignore)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->lintItem($php, $item, $itemPath)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run php -l against a specific file.
|
||||
* @param $php
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintFile($php, $path)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
if (!$this->phpci->executeCommand($php . ' -l "%s"', $this->phpci->buildPath . $path)) {
|
||||
$this->phpci->logFailure($path);
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
183
src/PHPCensor/Plugin/Mysql.php
Normal file
183
src/PHPCensor/Plugin/Mysql.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PDO;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
use b8\Database;
|
||||
|
||||
/**
|
||||
* MySQL Plugin - Provides access to a MySQL database.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @author Steve Kamerman <stevekamerman@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Mysql implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $queries = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pass;
|
||||
|
||||
/**
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->queries = $options;
|
||||
|
||||
$config = Database::getConnection('write')->getDetails();
|
||||
|
||||
$this->host =(defined('PHPCI_DB_HOST')) ? PHPCI_DB_HOST : null;
|
||||
$this->user = $config['user'];
|
||||
$this->pass = $config['pass'];
|
||||
|
||||
$buildSettings = $phpci->getConfig('build_settings');
|
||||
|
||||
if (!isset($buildSettings['mysql'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($buildSettings['mysql']['host'])) {
|
||||
$this->host = $this->phpci->interpolate($buildSettings['mysql']['host']);
|
||||
}
|
||||
|
||||
if (!empty($buildSettings['mysql']['user'])) {
|
||||
$this->user = $this->phpci->interpolate($buildSettings['mysql']['user']);
|
||||
}
|
||||
|
||||
if (array_key_exists('pass', $buildSettings['mysql'])) {
|
||||
$this->pass = $buildSettings['mysql']['pass'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to MySQL and runs a specified set of queries.
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
$opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION];
|
||||
$pdo = new PDO('mysql:host=' . $this->host, $this->user, $this->pass, $opts);
|
||||
|
||||
foreach ($this->queries as $query) {
|
||||
if (!is_array($query)) {
|
||||
// Simple query
|
||||
$pdo->query($this->phpci->interpolate($query));
|
||||
} elseif (isset($query['import'])) {
|
||||
// SQL file execution
|
||||
$this->executeFile($query['import']);
|
||||
} else {
|
||||
throw new \Exception(Lang::get('invalid_command'));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->phpci->logFailure($ex->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @return boolean
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function executeFile($query)
|
||||
{
|
||||
if (!isset($query['file'])) {
|
||||
throw new \Exception(Lang::get('import_file_key'));
|
||||
}
|
||||
|
||||
$import_file = $this->phpci->buildPath . $this->phpci->interpolate($query['file']);
|
||||
if (!is_readable($import_file)) {
|
||||
throw new \Exception(Lang::get('cannot_open_import', $import_file));
|
||||
}
|
||||
|
||||
$database = isset($query['database']) ? $this->phpci->interpolate($query['database']) : null;
|
||||
|
||||
$import_command = $this->getImportCommand($import_file, $database);
|
||||
if (!$this->phpci->executeCommand($import_command)) {
|
||||
throw new \Exception(Lang::get('unable_to_execute'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the MySQL import command required to import/execute the specified file
|
||||
*
|
||||
* @param string $import_file Path to file, relative to the build root
|
||||
* @param string $database If specified, this database is selected before execution
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getImportCommand($import_file, $database = null)
|
||||
{
|
||||
$decompression = [
|
||||
'bz2' => '| bzip2 --decompress',
|
||||
'gz' => '| gzip --decompress',
|
||||
];
|
||||
|
||||
$extension = strtolower(pathinfo($import_file, PATHINFO_EXTENSION));
|
||||
$decomp_cmd = '';
|
||||
if (array_key_exists($extension, $decompression)) {
|
||||
$decomp_cmd = $decompression[$extension];
|
||||
}
|
||||
|
||||
$args = [
|
||||
':import_file' => escapeshellarg($import_file),
|
||||
':decomp_cmd' => $decomp_cmd,
|
||||
':host' => escapeshellarg($this->host),
|
||||
':user' => escapeshellarg($this->user),
|
||||
':pass' => (!$this->pass) ? '' : '-p' . escapeshellarg($this->pass),
|
||||
':database' => ($database === null)? '': escapeshellarg($database),
|
||||
];
|
||||
|
||||
return strtr('cat :import_file :decomp_cmd | mysql -h:host -u:user :pass :database', $args);
|
||||
}
|
||||
}
|
||||
92
src/PHPCensor/Plugin/PackageBuild.php
Normal file
92
src/PHPCensor/Plugin/PackageBuild.php
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Create a ZIP or TAR.GZ archive of the entire build.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PackageBuild implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $filename;
|
||||
protected $format;
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->build = $build;
|
||||
$this->phpci = $phpci;
|
||||
$this->directory = isset($options['directory']) ? $options['directory'] : $path;
|
||||
$this->filename = isset($options['filename']) ? $options['filename'] : 'build';
|
||||
$this->format = isset($options['format']) ? $options['format'] : 'zip';
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Composer and runs a specified command (e.g. install / update)
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$path = $this->phpci->buildPath;
|
||||
$build = $this->build;
|
||||
|
||||
if ($this->directory == $path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filename = str_replace('%build.commit%', $build->getCommitId(), $this->filename);
|
||||
$filename = str_replace('%build.id%', $build->getId(), $filename);
|
||||
$filename = str_replace('%build.branch%', $build->getBranch(), $filename);
|
||||
$filename = str_replace('%project.title%', $build->getProject()->getTitle(), $filename);
|
||||
$filename = str_replace('%date%', date('Y-m-d'), $filename);
|
||||
$filename = str_replace('%time%', date('Hi'), $filename);
|
||||
$filename = preg_replace('/([^a-zA-Z0-9_-]+)/', '', $filename);
|
||||
|
||||
$curdir = getcwd();
|
||||
chdir($this->phpci->buildPath);
|
||||
|
||||
if (!is_array($this->format)) {
|
||||
$this->format = [$this->format];
|
||||
}
|
||||
|
||||
foreach ($this->format as $format) {
|
||||
switch ($format) {
|
||||
case 'tar':
|
||||
$cmd = 'tar cfz "%s/%s.tar.gz" ./*';
|
||||
break;
|
||||
default:
|
||||
case 'zip':
|
||||
$cmd = 'zip -rq "%s/%s.zip" ./*';
|
||||
break;
|
||||
}
|
||||
|
||||
$success = $this->phpci->executeCommand($cmd, $this->directory, $filename);
|
||||
}
|
||||
|
||||
chdir($curdir);
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
137
src/PHPCensor/Plugin/Pdepend.php
Normal file
137
src/PHPCensor/Plugin/Pdepend.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Pdepend Plugin - Allows Pdepend report
|
||||
* @author Johan van der Heide <info@japaveh.nl>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Pdepend implements Plugin
|
||||
{
|
||||
protected $args;
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
/**
|
||||
* @var string Directory which needs to be scanned
|
||||
*/
|
||||
protected $directory;
|
||||
/**
|
||||
* @var string File where the summary.xml is stored
|
||||
*/
|
||||
protected $summary;
|
||||
/**
|
||||
* @var string File where the chart.svg is stored
|
||||
*/
|
||||
protected $chart;
|
||||
/**
|
||||
* @var string File where the pyramid.svg is stored
|
||||
*/
|
||||
protected $pyramid;
|
||||
/**
|
||||
* @var string Location on the server where the files are stored. Preferably in the webroot for inclusion
|
||||
* in the readme.md of the repository
|
||||
*/
|
||||
protected $location;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath;
|
||||
|
||||
$title = $phpci->getBuildProjectTitle();
|
||||
$this->summary = $title . '-summary.xml';
|
||||
$this->pyramid = $title . '-pyramid.svg';
|
||||
$this->chart = $title . '-chart.svg';
|
||||
$this->location = $this->phpci->buildPath . '..' . DIRECTORY_SEPARATOR . 'pdepend';
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs Pdepend with the given criteria as arguments
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (!file_exists($this->location)) {
|
||||
mkdir($this->location);
|
||||
}
|
||||
if (!is_writable($this->location)) {
|
||||
throw new \Exception(sprintf('The location %s is not writable or does not exist.', $this->location));
|
||||
}
|
||||
|
||||
$pdepend = $this->phpci->findBinary('pdepend');
|
||||
|
||||
$cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"';
|
||||
|
||||
$this->removeBuildArtifacts();
|
||||
|
||||
// If we need to ignore directories
|
||||
if (count($this->phpci->ignore)) {
|
||||
$ignore = ' --ignore=' . implode(',', $this->phpci->ignore);
|
||||
} else {
|
||||
$ignore = '';
|
||||
}
|
||||
|
||||
$success = $this->phpci->executeCommand(
|
||||
$cmd,
|
||||
$this->location . DIRECTORY_SEPARATOR . $this->summary,
|
||||
$this->location . DIRECTORY_SEPARATOR . $this->chart,
|
||||
$this->location . DIRECTORY_SEPARATOR . $this->pyramid,
|
||||
$ignore,
|
||||
$this->directory
|
||||
);
|
||||
|
||||
$config = $this->phpci->getSystemConfig('phpci');
|
||||
|
||||
if ($success) {
|
||||
$this->phpci->logSuccess(
|
||||
sprintf(
|
||||
"Pdepend successful. You can use %s\n, \n
|
||||
and \n
|
||||
for inclusion in the readme.md file",
|
||||
$config['url'] . '/build/pdepend/' . $this->summary,
|
||||
$config['url'] . '/build/pdepend/' . $this->chart,
|
||||
$config['url'] . '/build/pdepend/' . $this->pyramid
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove files created from previous builds
|
||||
*/
|
||||
protected function removeBuildArtifacts()
|
||||
{
|
||||
//Remove the created files first
|
||||
foreach ([$this->summary, $this->chart, $this->pyramid] as $file) {
|
||||
if (file_exists($this->location . DIRECTORY_SEPARATOR . $file)) {
|
||||
unlink($this->location . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/PHPCensor/Plugin/Pgsql.php
Normal file
97
src/PHPCensor/Plugin/Pgsql.php
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PDO;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* PgSQL Plugin - Provides access to a PgSQL database.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Pgsql implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $queries = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pass;
|
||||
|
||||
/**
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->queries = $options;
|
||||
|
||||
$buildSettings = $phpci->getConfig('build_settings');
|
||||
|
||||
if (isset($buildSettings['pgsql'])) {
|
||||
$sql = $buildSettings['pgsql'];
|
||||
$this->host = $sql['host'];
|
||||
$this->user = $sql['user'];
|
||||
$this->pass = $sql['pass'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to PgSQL and runs a specified set of queries.
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
$opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION];
|
||||
$pdo = new PDO('pgsql:host=' . $this->host, $this->user, $this->pass, $opts);
|
||||
|
||||
foreach ($this->queries as $query) {
|
||||
$pdo->query($this->phpci->interpolate($query));
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->phpci->logFailure($ex->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
251
src/PHPCensor/Plugin/Phar.php
Normal file
251
src/PHPCensor/Plugin/Phar.php
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use Exception;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use Phar as PHPPhar;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Phar Plugin
|
||||
*/
|
||||
class Phar implements Plugin
|
||||
{
|
||||
/**
|
||||
* PHPCI
|
||||
* @var Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* Build
|
||||
* @var Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* Output Directory
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* Phar Filename
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Regular Expression Filename Capture
|
||||
* @var string
|
||||
*/
|
||||
protected $regexp;
|
||||
|
||||
/**
|
||||
* Stub Filename
|
||||
* @var string
|
||||
*/
|
||||
protected $stub;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
// Basic
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
// Directory?
|
||||
if (isset($options['directory'])) {
|
||||
$this->setDirectory($options['directory']);
|
||||
}
|
||||
|
||||
// Filename?
|
||||
if (isset($options['filename'])) {
|
||||
$this->setFilename($options['filename']);
|
||||
}
|
||||
|
||||
// RegExp?
|
||||
if (isset($options['regexp'])) {
|
||||
$this->setRegExp($options['regexp']);
|
||||
}
|
||||
|
||||
// Stub?
|
||||
if (isset($options['stub'])) {
|
||||
$this->setStub($options['stub']);
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PHPCI
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function getPHPCI()
|
||||
{
|
||||
return $this->phpci;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Build
|
||||
*
|
||||
* @return Build
|
||||
*/
|
||||
public function getBuild()
|
||||
{
|
||||
return $this->build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory Setter
|
||||
*
|
||||
* @param string $directory Configuration Value
|
||||
* @return Phar Fluent Interface
|
||||
*/
|
||||
public function setDirectory($directory)
|
||||
{
|
||||
$this->directory = $directory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory Getter
|
||||
*
|
||||
* @return string Configurated or Default Value
|
||||
*/
|
||||
public function getDirectory()
|
||||
{
|
||||
if (!isset($this->directory)) {
|
||||
$this->setDirectory($this->getPHPCI()->buildPath);
|
||||
}
|
||||
return $this->directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filename Setter
|
||||
*
|
||||
* @param string $filename Configuration Value
|
||||
* @return Phar Fluent Interface
|
||||
*/
|
||||
public function setFilename($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filename Getter
|
||||
*
|
||||
* @return string Configurated or Default Value
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
if (!isset($this->filename)) {
|
||||
$this->setFilename('build.phar');
|
||||
}
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular Expression Setter
|
||||
*
|
||||
* @param string $regexp Configuration Value
|
||||
* @return Phar Fluent Interface
|
||||
*/
|
||||
public function setRegExp($regexp)
|
||||
{
|
||||
$this->regexp = $regexp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular Expression Getter
|
||||
*
|
||||
* @return string Configurated or Default Value
|
||||
*/
|
||||
public function getRegExp()
|
||||
{
|
||||
if (!isset($this->regexp)) {
|
||||
$this->setRegExp('/\.php$/');
|
||||
}
|
||||
return $this->regexp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub Filename Setter
|
||||
*
|
||||
* @param string $stub Configuration Value
|
||||
* @return Phar Fluent Interface
|
||||
*/
|
||||
public function setStub($stub)
|
||||
{
|
||||
$this->stub = $stub;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub Filename Getter
|
||||
*
|
||||
* @return string Configurated Value
|
||||
*/
|
||||
public function getStub()
|
||||
{
|
||||
return $this->stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stub content for the Phar file.
|
||||
* @return string
|
||||
*/
|
||||
public function getStubContent()
|
||||
{
|
||||
$content = '';
|
||||
$filename = $this->getStub();
|
||||
if ($filename) {
|
||||
$content = file_get_contents($this->getPHPCI()->buildPath . DIRECTORY_SEPARATOR . $this->getStub());
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the phar plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$success = false;
|
||||
|
||||
try {
|
||||
$file = $this->getDirectory() . DIRECTORY_SEPARATOR . $this->getFilename();
|
||||
$phar = new PHPPhar($file, 0, $this->getFilename());
|
||||
$phar->buildFromDirectory($this->getPHPCI()->buildPath, $this->getRegExp());
|
||||
|
||||
$stub = $this->getStubContent();
|
||||
if ($stub) {
|
||||
$phar->setStub($stub);
|
||||
}
|
||||
|
||||
$success = true;
|
||||
} catch (Exception $e) {
|
||||
$this->getPHPCI()->log(Lang::get('phar_internal_error'));
|
||||
$this->getPHPCI()->log($e->getMessage());
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
265
src/PHPCensor/Plugin/Phing.php
Normal file
265
src/PHPCensor/Plugin/Phing.php
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Phing Plugin - Provides access to Phing functionality.
|
||||
*
|
||||
* @author Pavel Pavlov <ppavlov@alera.ru>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Phing implements Plugin
|
||||
{
|
||||
|
||||
private $directory;
|
||||
private $buildFile = 'build.xml';
|
||||
private $targets = ['build'];
|
||||
private $properties = [];
|
||||
private $propertyFile;
|
||||
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->setPhpci($phpci);
|
||||
$this->build = $build;
|
||||
|
||||
/*
|
||||
* Set working directory
|
||||
*/
|
||||
if (isset($options['directory'])) {
|
||||
$directory = $phpci->buildPath . DIRECTORY_SEPARATOR . $options['directory'];
|
||||
} else {
|
||||
$directory = $phpci->buildPath;
|
||||
}
|
||||
|
||||
$this->setDirectory($directory);
|
||||
|
||||
/*
|
||||
* Sen name of a non default build file
|
||||
*/
|
||||
if (isset($options['build_file'])) {
|
||||
$this->setBuildFile($options['build_file']);
|
||||
}
|
||||
|
||||
if (isset($options['targets'])) {
|
||||
$this->setTargets($options['targets']);
|
||||
}
|
||||
|
||||
if (isset($options['properties'])) {
|
||||
$this->setProperties($options['properties']);
|
||||
}
|
||||
|
||||
if (isset($options['property_file'])) {
|
||||
$this->setPropertyFile($options['property_file']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Phing and runs a specified targets
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$phingExecutable = $this->phpci->findBinary('phing');
|
||||
|
||||
$cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath();
|
||||
|
||||
if ($this->getPropertyFile()) {
|
||||
$cmd[] = '-propertyfile ' . $this->getPropertyFile();
|
||||
}
|
||||
|
||||
$cmd[] = $this->propertiesToString();
|
||||
|
||||
$cmd[] = '-logger phing.listener.DefaultLogger';
|
||||
$cmd[] = $this->targetsToString();
|
||||
$cmd[] = '2>&1';
|
||||
|
||||
return $this->phpci->executeCommand(implode(' ', $cmd), $this->directory, $this->targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPCI\Builder
|
||||
*/
|
||||
public function getPhpci()
|
||||
{
|
||||
return $this->phpci;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PHPCI\Builder $phpci
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPhpci($phpci)
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDirectory()
|
||||
{
|
||||
return $this->directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDirectory($directory)
|
||||
{
|
||||
$this->directory = $directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTargets()
|
||||
{
|
||||
return $this->targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of targets into a string.
|
||||
* @return string
|
||||
*/
|
||||
private function targetsToString()
|
||||
{
|
||||
return implode(' ', $this->targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $targets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTargets($targets)
|
||||
{
|
||||
if (is_string($targets)) {
|
||||
$targets = [$targets];
|
||||
}
|
||||
|
||||
$this->targets = $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBuildFile()
|
||||
{
|
||||
return $this->buildFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $buildFile
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setBuildFile($buildFile)
|
||||
{
|
||||
if (!file_exists($this->getDirectory() . $buildFile)) {
|
||||
throw new \Exception(Lang::get('build_file_missing'));
|
||||
}
|
||||
|
||||
$this->buildFile = $buildFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phing build file path.
|
||||
* @return string
|
||||
*/
|
||||
public function getBuildFilePath()
|
||||
{
|
||||
return $this->getDirectory() . $this->buildFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function propertiesToString()
|
||||
{
|
||||
/**
|
||||
* fix the problem when execute phing out of the build dir
|
||||
* @ticket 748
|
||||
*/
|
||||
if (!isset($this->properties['project.basedir'])) {
|
||||
$this->properties['project.basedir'] = $this->getDirectory();
|
||||
}
|
||||
|
||||
$propertiesString = [];
|
||||
|
||||
foreach ($this->properties as $name => $value) {
|
||||
$propertiesString[] = '-D' . $name . '="' . $value . '"';
|
||||
}
|
||||
|
||||
return implode(' ', $propertiesString);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $properties
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperties($properties)
|
||||
{
|
||||
if (is_string($properties)) {
|
||||
$properties = [$properties];
|
||||
}
|
||||
|
||||
$this->properties = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPropertyFile()
|
||||
{
|
||||
return $this->propertyFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $propertyFile
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setPropertyFile($propertyFile)
|
||||
{
|
||||
if (!file_exists($this->getDirectory() . DIRECTORY_SEPARATOR . $propertyFile)) {
|
||||
throw new \Exception(Lang::get('property_file_missing'));
|
||||
}
|
||||
|
||||
$this->propertyFile = $propertyFile;
|
||||
}
|
||||
}
|
||||
248
src/PHPCensor/Plugin/PhpCodeSniffer.php
Normal file
248
src/PHPCensor/Plugin/PhpCodeSniffer.php
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Model\BuildError;
|
||||
|
||||
/**
|
||||
* PHP Code Sniffer Plugin - Allows PHP Code Sniffer testing.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpCodeSniffer implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $suffixes;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $standard;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tab_width;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $allowed_errors;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $allowed_warnings;
|
||||
|
||||
/**
|
||||
* @var string, based on the assumption the root may not hold the code to be
|
||||
* tested, exteds the base path
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PHPCI\Builder $phpci
|
||||
* @param \PHPCI\Model\Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->suffixes = ['php'];
|
||||
$this->directory = $phpci->buildPath;
|
||||
$this->standard = 'PSR2';
|
||||
$this->tab_width = '';
|
||||
$this->encoding = '';
|
||||
$this->path = '';
|
||||
$this->ignore = $this->phpci->ignore;
|
||||
$this->allowed_warnings = 0;
|
||||
$this->allowed_errors = 0;
|
||||
|
||||
if (isset($options['zero_config']) && $options['zero_config']) {
|
||||
$this->allowed_warnings = -1;
|
||||
$this->allowed_errors = -1;
|
||||
}
|
||||
|
||||
if (isset($options['suffixes'])) {
|
||||
$this->suffixes = (array)$options['suffixes'];
|
||||
}
|
||||
|
||||
if (!empty($options['tab_width'])) {
|
||||
$this->tab_width = ' --tab-width='.$options['tab_width'];
|
||||
}
|
||||
|
||||
if (!empty($options['encoding'])) {
|
||||
$this->encoding = ' --encoding=' . $options['encoding'];
|
||||
}
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle this plugin's options.
|
||||
* @param $options
|
||||
*/
|
||||
protected function setOptions($options)
|
||||
{
|
||||
foreach (['directory', 'standard', 'path', 'ignore', 'allowed_warnings', 'allowed_errors'] as $key) {
|
||||
if (array_key_exists($key, $options)) {
|
||||
$this->{$key} = $options[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Code Sniffer in a specified directory, to a specified standard.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
list($ignore, $standard, $suffixes) = $this->getFlags();
|
||||
|
||||
$phpcs = $this->phpci->findBinary('phpcs');
|
||||
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
$cmd = $phpcs . ' --report=json %s %s %s %s %s "%s"';
|
||||
$this->phpci->executeCommand(
|
||||
$cmd,
|
||||
$standard,
|
||||
$suffixes,
|
||||
$ignore,
|
||||
$this->tab_width,
|
||||
$this->encoding,
|
||||
$this->phpci->buildPath . $this->path
|
||||
);
|
||||
|
||||
$output = $this->phpci->getLastOutput();
|
||||
list($errors, $warnings) = $this->processReport($output);
|
||||
|
||||
$this->phpci->logExecOutput(true);
|
||||
|
||||
$success = true;
|
||||
$this->build->storeMeta('phpcs-warnings', $warnings);
|
||||
$this->build->storeMeta('phpcs-errors', $errors);
|
||||
|
||||
if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process options and produce an arguments string for PHPCS.
|
||||
* @return array
|
||||
*/
|
||||
protected function getFlags()
|
||||
{
|
||||
$ignore = '';
|
||||
if (count($this->ignore)) {
|
||||
$ignore = ' --ignore=' . implode(',', $this->ignore);
|
||||
}
|
||||
|
||||
if (strpos($this->standard, '/') !== false) {
|
||||
$standard = ' --standard='.$this->directory.$this->standard;
|
||||
} else {
|
||||
$standard = ' --standard='.$this->standard;
|
||||
}
|
||||
|
||||
$suffixes = '';
|
||||
if (count($this->suffixes)) {
|
||||
$suffixes = ' --extensions=' . implode(',', $this->suffixes);
|
||||
}
|
||||
|
||||
return [$ignore, $standard, $suffixes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the PHPCS output report.
|
||||
* @param $output
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function processReport($output)
|
||||
{
|
||||
$data = json_decode(trim($output), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
$this->phpci->log($output);
|
||||
throw new \Exception(PHPCensor\Helper\Lang::get('could_not_process_report'));
|
||||
}
|
||||
|
||||
$errors = $data['totals']['errors'];
|
||||
$warnings = $data['totals']['warnings'];
|
||||
|
||||
foreach ($data['files'] as $fileName => $file) {
|
||||
$fileName = str_replace($this->phpci->buildPath, '', $fileName);
|
||||
|
||||
foreach ($file['messages'] as $message) {
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'php_code_sniffer',
|
||||
'PHPCS: ' . $message['message'],
|
||||
$message['type'] == 'ERROR' ? BuildError::SEVERITY_HIGH : BuildError::SEVERITY_LOW,
|
||||
$fileName,
|
||||
$message['line']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [$errors, $warnings];
|
||||
}
|
||||
}
|
||||
157
src/PHPCensor/Plugin/PhpCpd.php
Normal file
157
src/PHPCensor/Plugin/PhpCpd.php
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Model\BuildError;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* PHP Copy / Paste Detector - Allows PHP Copy / Paste Detector testing.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpCpd implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $args;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string, based on the assumption the root may not hold the code to be
|
||||
* tested, extends the base path
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->path = $phpci->buildPath;
|
||||
$this->standard = 'PSR1';
|
||||
$this->ignore = $phpci->ignore;
|
||||
|
||||
if (!empty($options['path'])) {
|
||||
$this->path = $phpci->buildPath . $options['path'];
|
||||
}
|
||||
|
||||
if (!empty($options['standard'])) {
|
||||
$this->standard = $options['standard'];
|
||||
}
|
||||
|
||||
if (!empty($options['ignore'])) {
|
||||
$this->ignore = $options['ignore'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Copy/Paste Detector in a specified directory.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$ignore = '';
|
||||
if (count($this->ignore)) {
|
||||
$map = function ($item) {
|
||||
// remove the trailing slash
|
||||
$item = rtrim($item, DIRECTORY_SEPARATOR);
|
||||
|
||||
if (is_file(rtrim($this->path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item)) {
|
||||
return ' --names-exclude ' . $item;
|
||||
} else {
|
||||
return ' --exclude ' . $item;
|
||||
}
|
||||
|
||||
};
|
||||
$ignore = array_map($map, $this->ignore);
|
||||
|
||||
$ignore = implode('', $ignore);
|
||||
}
|
||||
|
||||
$phpcpd = $this->phpci->findBinary('phpcpd');
|
||||
|
||||
$tmpfilename = tempnam('/tmp', 'phpcpd');
|
||||
|
||||
$cmd = $phpcpd . ' --log-pmd "%s" %s "%s"';
|
||||
$success = $this->phpci->executeCommand($cmd, $tmpfilename, $ignore, $this->path);
|
||||
|
||||
print $this->phpci->getLastOutput();
|
||||
|
||||
$errorCount = $this->processReport(file_get_contents($tmpfilename));
|
||||
$this->build->storeMeta('phpcpd-warnings', $errorCount);
|
||||
|
||||
unlink($tmpfilename);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the PHPCPD XML report.
|
||||
* @param $xmlString
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function processReport($xmlString)
|
||||
{
|
||||
$xml = simplexml_load_string($xmlString);
|
||||
|
||||
if ($xml === false) {
|
||||
$this->phpci->log($xmlString);
|
||||
throw new \Exception(Lang::get('could_not_process_report'));
|
||||
}
|
||||
|
||||
$warnings = 0;
|
||||
foreach ($xml->duplication as $duplication) {
|
||||
foreach ($duplication->file as $file) {
|
||||
$fileName = (string)$file['path'];
|
||||
$fileName = str_replace($this->phpci->buildPath, '', $fileName);
|
||||
|
||||
$message = <<<CPD
|
||||
Copy and paste detected:
|
||||
|
||||
```
|
||||
{$duplication->codefragment}
|
||||
```
|
||||
CPD;
|
||||
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'php_cpd',
|
||||
$message,
|
||||
BuildError::SEVERITY_NORMAL,
|
||||
$fileName,
|
||||
(int) $file['line'],
|
||||
(int) $file['line'] + (int) $duplication['lines']
|
||||
);
|
||||
}
|
||||
|
||||
$warnings++;
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
}
|
||||
}
|
||||
105
src/PHPCensor/Plugin/PhpCsFixer.php
Normal file
105
src/PHPCensor/Plugin/PhpCsFixer.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* PHP CS Fixer - Works with the PHP Coding Standards Fixer for testing coding standards.
|
||||
* @author Gabriel Baker <gabriel@autonomicpilot.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpCsFixer implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
protected $workingDir = '';
|
||||
protected $level = ' --level=psr2';
|
||||
protected $verbose = '';
|
||||
protected $diff = '';
|
||||
protected $levels = ['psr0', 'psr1', 'psr2', 'symfony'];
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->workingdir = $this->phpci->buildPath;
|
||||
$this->buildArgs($options);
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run PHP CS Fixer.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$curdir = getcwd();
|
||||
chdir($this->workingdir);
|
||||
|
||||
$phpcsfixer = $this->phpci->findBinary('php-cs-fixer');
|
||||
|
||||
$cmd = $phpcsfixer . ' fix . %s %s %s';
|
||||
$success = $this->phpci->executeCommand($cmd, $this->verbose, $this->diff, $this->level);
|
||||
|
||||
chdir($curdir);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an args string for PHPCS Fixer.
|
||||
* @param $options
|
||||
*/
|
||||
public function buildArgs($options)
|
||||
{
|
||||
if (isset($options['verbose']) && $options['verbose']) {
|
||||
$this->verbose = ' --verbose';
|
||||
}
|
||||
|
||||
if (isset($options['diff']) && $options['diff']) {
|
||||
$this->diff = ' --diff';
|
||||
}
|
||||
|
||||
if (isset($options['level']) && in_array($options['level'], $this->levels)) {
|
||||
$this->level = ' --level='.$options['level'];
|
||||
}
|
||||
|
||||
if (isset($options['workingdir']) && $options['workingdir']) {
|
||||
$this->workingdir = $this->phpci->buildPath . $options['workingdir'];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
182
src/PHPCensor/Plugin/PhpDocblockChecker.php
Normal file
182
src/PHPCensor/Plugin/PhpDocblockChecker.php
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHP Docblock Checker Plugin - Checks your PHP files for appropriate uses of Docblocks
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpDocblockChecker implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string Based on the assumption the root may not hold the code to be
|
||||
* tested, extends the build path.
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
protected $skipClasses = false;
|
||||
protected $skipMethods = false;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->ignore = $phpci->ignore;
|
||||
$this->path = '';
|
||||
$this->allowed_warnings = 0;
|
||||
|
||||
if (isset($options['zero_config']) && $options['zero_config']) {
|
||||
$this->allowed_warnings = -1;
|
||||
}
|
||||
|
||||
if (array_key_exists('skip_classes', $options)) {
|
||||
$this->skipClasses = true;
|
||||
}
|
||||
|
||||
if (array_key_exists('skip_methods', $options)) {
|
||||
$this->skipMethods = true;
|
||||
}
|
||||
|
||||
if (!empty($options['path'])) {
|
||||
$this->path = $options['path'];
|
||||
}
|
||||
|
||||
if (array_key_exists('allowed_warnings', $options)) {
|
||||
$this->allowed_warnings = (int)$options['allowed_warnings'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Mess Detector in a specified directory.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// Check that the binary exists:
|
||||
$checker = $this->phpci->findBinary('phpdoccheck');
|
||||
|
||||
// Build ignore string:
|
||||
$ignore = '';
|
||||
if (count($this->ignore)) {
|
||||
$ignore = ' --exclude="' . implode(',', $this->ignore) . '"';
|
||||
}
|
||||
|
||||
// Are we skipping any checks?
|
||||
$add = '';
|
||||
if ($this->skipClasses) {
|
||||
$add .= ' --skip-classes';
|
||||
}
|
||||
|
||||
if ($this->skipMethods) {
|
||||
$add .= ' --skip-methods';
|
||||
}
|
||||
|
||||
// Build command string:
|
||||
$path = $this->phpci->buildPath . $this->path;
|
||||
$cmd = $checker . ' --json --directory="%s"%s%s';
|
||||
|
||||
// Disable exec output logging, as we don't want the XML report in the log:
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
// Run checker:
|
||||
$this->phpci->executeCommand(
|
||||
$cmd,
|
||||
$path,
|
||||
$ignore,
|
||||
$add
|
||||
);
|
||||
|
||||
// Re-enable exec output logging:
|
||||
$this->phpci->logExecOutput(true);
|
||||
|
||||
$output = json_decode($this->phpci->getLastOutput(), true);
|
||||
$errors = count($output);
|
||||
$success = true;
|
||||
|
||||
$this->build->storeMeta('phpdoccheck-warnings', $errors);
|
||||
$this->reportErrors($output);
|
||||
|
||||
if ($this->allowed_warnings != -1 && $errors > $this->allowed_warnings) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report all of the errors we've encountered line-by-line.
|
||||
* @param $output
|
||||
*/
|
||||
protected function reportErrors($output)
|
||||
{
|
||||
foreach ($output as $error) {
|
||||
$message = 'Class ' . $error['class'] . ' is missing a docblock.';
|
||||
$severity = PHPCensor\Model\BuildError::SEVERITY_LOW;
|
||||
|
||||
if ($error['type'] == 'method') {
|
||||
$message = $error['class'] . '::' . $error['method'] . ' is missing a docblock.';
|
||||
$severity = PHPCensor\Model\BuildError::SEVERITY_NORMAL;
|
||||
}
|
||||
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'php_docblock_checker',
|
||||
$message,
|
||||
$severity,
|
||||
$error['file'],
|
||||
$error['line']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/PHPCensor/Plugin/PhpLoc.php
Normal file
100
src/PHPCensor/Plugin/PhpLoc.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHP Loc - Allows PHP Copy / Lines of Code testing.
|
||||
* @author Johan van der Heide <info@japaveh.nl>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpLoc implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directory = $phpci->buildPath;
|
||||
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory .= $options['directory'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Copy/Paste Detector in a specified directory.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$ignore = '';
|
||||
|
||||
if (count($this->phpci->ignore)) {
|
||||
$map = function ($item) {
|
||||
return ' --exclude ' . rtrim($item, DIRECTORY_SEPARATOR);
|
||||
};
|
||||
|
||||
$ignore = array_map($map, $this->phpci->ignore);
|
||||
$ignore = implode('', $ignore);
|
||||
}
|
||||
|
||||
$phploc = $this->phpci->findBinary('phploc');
|
||||
|
||||
$success = $this->phpci->executeCommand($phploc . ' %s "%s"', $ignore, $this->directory);
|
||||
$output = $this->phpci->getLastOutput();
|
||||
|
||||
if (preg_match_all('/\((LOC|CLOC|NCLOC|LLOC)\)\s+([0-9]+)/', $output, $matches)) {
|
||||
$data = [];
|
||||
foreach ($matches[1] as $k => $v) {
|
||||
$data[$v] = (int)$matches[2][$k];
|
||||
}
|
||||
|
||||
$this->build->storeMeta('phploc', $data);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
271
src/PHPCensor/Plugin/PhpMessDetector.php
Normal file
271
src/PHPCensor/Plugin/PhpMessDetector.php
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHP Mess Detector Plugin - Allows PHP Mess Detector testing.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpMessDetector implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $suffixes;
|
||||
|
||||
/**
|
||||
* @var string, based on the assumption the root may not hold the code to be
|
||||
* tested, extends the base path only if the provided path is relative. Absolute
|
||||
* paths are used verbatim
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* Array of PHPMD rules. Can be one of the builtins (codesize, unusedcode, naming, design, controversial)
|
||||
* or a filename (detected by checking for a / in it), either absolute or relative to the project root.
|
||||
* @var array
|
||||
*/
|
||||
protected $rules;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->suffixes = ['php'];
|
||||
$this->ignore = $phpci->ignore;
|
||||
$this->path = '';
|
||||
$this->rules = ['codesize', 'unusedcode', 'naming'];
|
||||
$this->allowed_warnings = 0;
|
||||
|
||||
if (isset($options['zero_config']) && $options['zero_config']) {
|
||||
$this->allowed_warnings = -1;
|
||||
}
|
||||
|
||||
if (!empty($options['path'])) {
|
||||
$this->path = $options['path'];
|
||||
}
|
||||
|
||||
if (array_key_exists('allowed_warnings', $options)) {
|
||||
$this->allowed_warnings = (int)$options['allowed_warnings'];
|
||||
}
|
||||
|
||||
foreach (['rules', 'ignore', 'suffixes'] as $key) {
|
||||
$this->overrideSetting($options, $key);
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Mess Detector in a specified directory.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (!$this->tryAndProcessRules()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$phpmdBinaryPath = $this->phpci->findBinary('phpmd');
|
||||
|
||||
$this->executePhpMd($phpmdBinaryPath);
|
||||
|
||||
$errorCount = $this->processReport(trim($this->phpci->getLastOutput()));
|
||||
$this->build->storeMeta('phpmd-warnings', $errorCount);
|
||||
|
||||
return $this->wasLastExecSuccessful($errorCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override a default setting.
|
||||
* @param $options
|
||||
* @param $key
|
||||
*/
|
||||
protected function overrideSetting($options, $key)
|
||||
{
|
||||
if (isset($options[$key]) && is_array($options[$key])) {
|
||||
$this->{$key} = $options[$key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process PHPMD's XML output report.
|
||||
* @param $xmlString
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function processReport($xmlString)
|
||||
{
|
||||
$xml = simplexml_load_string($xmlString);
|
||||
|
||||
if ($xml === false) {
|
||||
$this->phpci->log($xmlString);
|
||||
throw new \Exception('Could not process PHPMD report XML.');
|
||||
}
|
||||
|
||||
$warnings = 0;
|
||||
|
||||
foreach ($xml->file as $file) {
|
||||
$fileName = (string)$file['name'];
|
||||
$fileName = str_replace($this->phpci->buildPath, '', $fileName);
|
||||
|
||||
foreach ($file->violation as $violation) {
|
||||
$warnings++;
|
||||
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'php_mess_detector',
|
||||
(string)$violation,
|
||||
PHPCensor\Model\BuildError::SEVERITY_HIGH,
|
||||
$fileName,
|
||||
(int)$violation['beginline'],
|
||||
(int)$violation['endline']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try and process the rules parameter from phpci.yml.
|
||||
* @return bool
|
||||
*/
|
||||
protected function tryAndProcessRules()
|
||||
{
|
||||
if (!empty($this->rules) && !is_array($this->rules)) {
|
||||
$this->phpci->logFailure('The "rules" option must be an array.');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->rules as &$rule) {
|
||||
if (strpos($rule, '/') !== false) {
|
||||
$rule = $this->phpci->buildPath . $rule;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute PHP Mess Detector.
|
||||
* @param $binaryPath
|
||||
*/
|
||||
protected function executePhpMd($binaryPath)
|
||||
{
|
||||
$cmd = $binaryPath . ' "%s" xml %s %s %s';
|
||||
|
||||
$path = $this->getTargetPath();
|
||||
|
||||
$ignore = '';
|
||||
if (count($this->ignore)) {
|
||||
$ignore = ' --exclude ' . implode(',', $this->ignore);
|
||||
}
|
||||
|
||||
$suffixes = '';
|
||||
if (count($this->suffixes)) {
|
||||
$suffixes = ' --suffixes ' . implode(',', $this->suffixes);
|
||||
}
|
||||
|
||||
// Disable exec output logging, as we don't want the XML report in the log:
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
// Run PHPMD:
|
||||
$this->phpci->executeCommand(
|
||||
$cmd,
|
||||
$path,
|
||||
implode(',', $this->rules),
|
||||
$ignore,
|
||||
$suffixes
|
||||
);
|
||||
|
||||
// Re-enable exec output logging:
|
||||
$this->phpci->logExecOutput(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path PHPMD should be run against.
|
||||
* @return string
|
||||
*/
|
||||
protected function getTargetPath()
|
||||
{
|
||||
$path = $this->phpci->buildPath . $this->path;
|
||||
if (!empty($this->path) && $this->path{0} == '/') {
|
||||
$path = $this->path;
|
||||
return $path;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating if the error count can be considered a success.
|
||||
*
|
||||
* @param int $errorCount
|
||||
* @return bool
|
||||
*/
|
||||
protected function wasLastExecSuccessful($errorCount)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
if ($this->allowed_warnings != -1 && $errorCount > $this->allowed_warnings) {
|
||||
$success = false;
|
||||
return $success;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
114
src/PHPCensor/Plugin/PhpParallelLint.php
Normal file
114
src/PHPCensor/Plugin/PhpParallelLint.php
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Php Parallel Lint Plugin - Provides access to PHP lint functionality.
|
||||
* @author Vaclav Makes <vaclav@makes.cz>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpParallelLint implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directory = $phpci->buildPath;
|
||||
$this->ignore = $this->phpci->ignore;
|
||||
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory = $phpci->buildPath.$options['directory'];
|
||||
}
|
||||
|
||||
if (isset($options['ignore'])) {
|
||||
$this->ignore = $options['ignore'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes parallel lint
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
list($ignore) = $this->getFlags();
|
||||
|
||||
$phplint = $this->phpci->findBinary('parallel-lint');
|
||||
|
||||
$cmd = $phplint . ' %s "%s"';
|
||||
$success = $this->phpci->executeCommand(
|
||||
$cmd,
|
||||
$ignore,
|
||||
$this->directory
|
||||
);
|
||||
|
||||
$output = $this->phpci->getLastOutput();
|
||||
|
||||
$matches = [];
|
||||
if (preg_match_all('/Parse error\:/', $output, $matches)) {
|
||||
$this->build->storeMeta('phplint-errors', count($matches[0]));
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce an argument string for PHP Parallel Lint.
|
||||
* @return array
|
||||
*/
|
||||
protected function getFlags()
|
||||
{
|
||||
$ignoreFlags = [];
|
||||
foreach ($this->ignore as $ignoreDir) {
|
||||
$ignoreFlags[] = '--exclude "' . $this->phpci->buildPath . $ignoreDir . '"';
|
||||
}
|
||||
$ignore = implode(' ', $ignoreFlags);
|
||||
|
||||
return [$ignore];
|
||||
}
|
||||
}
|
||||
149
src/PHPCensor/Plugin/PhpSpec.php
Normal file
149
src/PHPCensor/Plugin/PhpSpec.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHP Spec Plugin - Allows PHP Spec testing.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpSpec implements PHPCensor\Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->options = $options;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Spec tests.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$curdir = getcwd();
|
||||
chdir($this->phpci->buildPath);
|
||||
|
||||
$phpspec = $this->phpci->findBinary(['phpspec', 'phpspec.php']);
|
||||
|
||||
$success = $this->phpci->executeCommand($phpspec . ' --format=junit --no-code-generation run');
|
||||
$output = $this->phpci->getLastOutput();
|
||||
|
||||
chdir($curdir);
|
||||
|
||||
/*
|
||||
* process xml output
|
||||
*
|
||||
* <testsuites time=FLOAT tests=INT failures=INT errors=INT>
|
||||
* <testsuite name=STRING time=FLOAT tests=INT failures=INT errors=INT skipped=INT>
|
||||
* <testcase name=STRING time=FLOAT classname=STRING status=STRING/>
|
||||
* </testsuite>
|
||||
* </testsuites
|
||||
*/
|
||||
|
||||
$xml = new \SimpleXMLElement($output);
|
||||
$attr = $xml->attributes();
|
||||
$data = [
|
||||
'time' => (float)$attr['time'],
|
||||
'tests' => (int)$attr['tests'],
|
||||
'failures' => (int)$attr['failures'],
|
||||
'errors' => (int)$attr['errors'],
|
||||
// now all the tests
|
||||
'suites' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \SimpleXMLElement $group
|
||||
*/
|
||||
foreach ($xml->xpath('testsuite') as $group) {
|
||||
$attr = $group->attributes();
|
||||
$suite = [
|
||||
'name' => (String)$attr['name'],
|
||||
'time' => (float)$attr['time'],
|
||||
'tests' => (int)$attr['tests'],
|
||||
'failures' => (int)$attr['failures'],
|
||||
'errors' => (int)$attr['errors'],
|
||||
'skipped' => (int)$attr['skipped'],
|
||||
// now the cases
|
||||
'cases' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
foreach ($group->xpath('testcase') as $child) {
|
||||
$attr = $child->attributes();
|
||||
$case = [
|
||||
'name' => (String)$attr['name'],
|
||||
'classname' => (String)$attr['classname'],
|
||||
'time' => (float)$attr['time'],
|
||||
'status' => (String)$attr['status'],
|
||||
];
|
||||
|
||||
if ($case['status']=='failed') {
|
||||
$error = [];
|
||||
/*
|
||||
* ok, sad, we had an error
|
||||
*
|
||||
* there should be one - foreach makes this easier
|
||||
*/
|
||||
foreach ($child->xpath('failure') as $failure) {
|
||||
$attr = $failure->attributes();
|
||||
$error['type'] = (String)$attr['type'];
|
||||
$error['message'] = (String)$attr['message'];
|
||||
}
|
||||
|
||||
foreach ($child->xpath('system-err') as $system_err) {
|
||||
$error['raw'] = (String)$system_err;
|
||||
}
|
||||
|
||||
$case['error'] = $error;
|
||||
}
|
||||
|
||||
$suite['cases'][] = $case;
|
||||
}
|
||||
|
||||
$data['suites'][] = $suite;
|
||||
}
|
||||
|
||||
$this->build->storeMeta('phpspec', $data);
|
||||
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
267
src/PHPCensor/Plugin/PhpTalLint.php
Normal file
267
src/PHPCensor/Plugin/PhpTalLint.php
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* PHPTAL Lint Plugin - Provides access to PHPTAL lint functionality.
|
||||
* @author Stephen Ball <phpci@stephen.rebelinblue.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpTalLint implements PHPCensor\Plugin
|
||||
{
|
||||
protected $directories;
|
||||
protected $recursive = true;
|
||||
protected $suffixes;
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string The path to a file contain custom phptal_tales_ functions
|
||||
*/
|
||||
protected $tales;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $allowed_warnings;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $allowed_errors;
|
||||
|
||||
/**
|
||||
* @var array The results of the lint scan
|
||||
*/
|
||||
protected $failedPaths = [];
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directories = [''];
|
||||
$this->suffixes = ['zpt'];
|
||||
$this->ignore = $phpci->ignore;
|
||||
|
||||
$this->allowed_warnings = 0;
|
||||
$this->allowed_errors = 0;
|
||||
|
||||
if (!empty($options['directory'])) {
|
||||
$this->directories = [$options['directory']];
|
||||
}
|
||||
|
||||
if (isset($options['suffixes'])) {
|
||||
$this->suffixes = (array)$options['suffixes'];
|
||||
}
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle this plugin's options.
|
||||
* @param $options
|
||||
*/
|
||||
protected function setOptions($options)
|
||||
{
|
||||
foreach (['directories', 'tales', 'allowed_warnings', 'allowed_errors'] as $key) {
|
||||
if (array_key_exists($key, $options)) {
|
||||
$this->{$key} = $options[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes phptal lint
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$this->phpci->quiet = true;
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
foreach ($this->directories as $dir) {
|
||||
$this->lintDirectory($dir);
|
||||
}
|
||||
|
||||
$this->phpci->quiet = false;
|
||||
$this->phpci->logExecOutput(true);
|
||||
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
foreach ($this->failedPaths as $path) {
|
||||
if ($path['type'] == 'error') {
|
||||
$errors++;
|
||||
} else {
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->build->storeMeta('phptallint-warnings', $warnings);
|
||||
$this->build->storeMeta('phptallint-errors', $errors);
|
||||
$this->build->storeMeta('phptallint-data', $this->failedPaths);
|
||||
|
||||
$success = true;
|
||||
|
||||
if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lint an item (file or directory) by calling the appropriate method.
|
||||
* @param $item
|
||||
* @param $itemPath
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintItem($item, $itemPath)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
if ($item->isFile() && in_array(strtolower($item->getExtension()), $this->suffixes)) {
|
||||
if (!$this->lintFile($itemPath)) {
|
||||
$success = false;
|
||||
}
|
||||
} elseif ($item->isDir() && $this->recursive && !$this->lintDirectory($itemPath . DIRECTORY_SEPARATOR)) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run phptal lint against a directory of files.
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintDirectory($path)
|
||||
{
|
||||
$success = true;
|
||||
$directory = new \DirectoryIterator($this->phpci->buildPath . $path);
|
||||
|
||||
foreach ($directory as $item) {
|
||||
if ($item->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemPath = $path . $item->getFilename();
|
||||
|
||||
if (in_array($itemPath, $this->ignore)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->lintItem($item, $itemPath)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run phptal lint against a specific file.
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function lintFile($path)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
list($suffixes, $tales) = $this->getFlags();
|
||||
|
||||
$lint = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
|
||||
$lint .= 'vendor' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR;
|
||||
$lint .= 'tools' . DIRECTORY_SEPARATOR . 'phptal_lint.php';
|
||||
$cmd = '/usr/bin/env php ' . $lint . ' %s %s "%s"';
|
||||
|
||||
$this->phpci->executeCommand($cmd, $suffixes, $tales, $this->phpci->buildPath . $path);
|
||||
|
||||
$output = $this->phpci->getLastOutput();
|
||||
|
||||
if (preg_match('/Found (.+?) (error|warning)/i', $output, $matches)) {
|
||||
$rows = explode(PHP_EOL, $output);
|
||||
|
||||
unset($rows[0]);
|
||||
unset($rows[1]);
|
||||
unset($rows[2]);
|
||||
unset($rows[3]);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$name = basename($path);
|
||||
|
||||
$row = str_replace('(use -i to include your custom modifier functions)', '', $row);
|
||||
$message = str_replace($name . ': ', '', $row);
|
||||
|
||||
$parts = explode(' (line ', $message);
|
||||
|
||||
$message = trim($parts[0]);
|
||||
$line = str_replace(')', '', $parts[1]);
|
||||
|
||||
$this->failedPaths[] = [
|
||||
'file' => $path,
|
||||
'line' => $line,
|
||||
'type' => $matches[2],
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process options and produce an arguments string for PHPTAL Lint.
|
||||
* @return array
|
||||
*/
|
||||
protected function getFlags()
|
||||
{
|
||||
$tales = '';
|
||||
if (!empty($this->tales)) {
|
||||
$tales = ' -i ' . $this->phpci->buildPath . $this->tales;
|
||||
}
|
||||
|
||||
$suffixes = '';
|
||||
if (count($this->suffixes)) {
|
||||
$suffixes = ' -e ' . implode(',', $this->suffixes);
|
||||
}
|
||||
|
||||
return [$suffixes, $tales];
|
||||
}
|
||||
}
|
||||
250
src/PHPCensor/Plugin/PhpUnit.php
Normal file
250
src/PHPCensor/Plugin/PhpUnit.php
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin\Util\TapParser;
|
||||
|
||||
/**
|
||||
* PHP Unit Plugin - Allows PHP Unit testing.
|
||||
* @author Dan Cryer <dan@block8.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class PhpUnit implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
protected $args;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string|string[] $directory The directory (or array of dirs) to run PHPUnit on
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* @var string $runFrom When running PHPUnit with an XML config, the command is run from this directory
|
||||
*/
|
||||
protected $runFrom;
|
||||
|
||||
/**
|
||||
* @var string, in cases where tests files are in a sub path of the /tests path,
|
||||
* allows this path to be set in the config.
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
protected $coverage = "";
|
||||
|
||||
/**
|
||||
* @var string|string[] $xmlConfigFile The path (or array of paths) of an xml config for PHPUnit
|
||||
*/
|
||||
protected $xmlConfigFile;
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test' && !is_null(self::findConfigFile($builder->buildPath))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try and find the phpunit XML config file.
|
||||
* @param $buildPath
|
||||
* @return null|string
|
||||
*/
|
||||
public static function findConfigFile($buildPath)
|
||||
{
|
||||
if (file_exists($buildPath . 'phpunit.xml')) {
|
||||
return 'phpunit.xml';
|
||||
}
|
||||
|
||||
if (file_exists($buildPath . 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml')) {
|
||||
return 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml';
|
||||
}
|
||||
|
||||
if (file_exists($buildPath . 'phpunit.xml.dist')) {
|
||||
return 'phpunit.xml.dist';
|
||||
}
|
||||
|
||||
if (file_exists($buildPath . 'tests/phpunit.xml.dist')) {
|
||||
return 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml.dist';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
if (empty($options['config']) && empty($options['directory'])) {
|
||||
$this->xmlConfigFile = self::findConfigFile($phpci->buildPath);
|
||||
}
|
||||
|
||||
if (isset($options['directory'])) {
|
||||
$this->directory = $options['directory'];
|
||||
}
|
||||
|
||||
if (isset($options['config'])) {
|
||||
$this->xmlConfigFile = $options['config'];
|
||||
}
|
||||
|
||||
if (isset($options['run_from'])) {
|
||||
$this->runFrom = $options['run_from'];
|
||||
}
|
||||
|
||||
if (isset($options['args'])) {
|
||||
$this->args = $this->phpci->interpolate($options['args']);
|
||||
}
|
||||
|
||||
if (isset($options['path'])) {
|
||||
$this->path = $options['path'];
|
||||
}
|
||||
|
||||
if (isset($options['coverage'])) {
|
||||
$this->coverage = ' --coverage-html ' . $this->phpci->interpolate($options['coverage']) . ' ';
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PHP Unit tests in a specified directory, optionally using specified config file(s).
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (empty($this->xmlConfigFile) && empty($this->directory)) {
|
||||
$this->phpci->logFailure('Neither configuration file nor test directory found.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
// Run any config files first. This can be either a single value or an array.
|
||||
if ($this->xmlConfigFile !== null) {
|
||||
$success &= $this->runConfigFile($this->xmlConfigFile);
|
||||
}
|
||||
|
||||
// Run any dirs next. Again this can be either a single value or an array.
|
||||
if ($this->directory !== null) {
|
||||
$success &= $this->runDir($this->directory);
|
||||
}
|
||||
|
||||
$tapString = $this->phpci->getLastOutput();
|
||||
$tapString = mb_convert_encoding($tapString, "UTF-8", "ISO-8859-1");
|
||||
|
||||
try {
|
||||
$tapParser = new TapParser($tapString);
|
||||
$output = $tapParser->parse();
|
||||
} catch (\Exception $ex) {
|
||||
$this->phpci->logFailure($tapString);
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$failures = $tapParser->getTotalFailures();
|
||||
|
||||
$this->build->storeMeta('phpunit-errors', $failures);
|
||||
$this->build->storeMeta('phpunit-data', $output);
|
||||
|
||||
$this->phpci->logExecOutput(true);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the tests defined in a PHPUnit config file.
|
||||
* @param $configPath
|
||||
* @return bool|mixed
|
||||
*/
|
||||
protected function runConfigFile($configPath)
|
||||
{
|
||||
if (is_array($configPath)) {
|
||||
return $this->recurseArg($configPath, [$this, "runConfigFile"]);
|
||||
} else {
|
||||
if ($this->runFrom) {
|
||||
$curdir = getcwd();
|
||||
chdir($this->phpci->buildPath . DIRECTORY_SEPARATOR . $this->runFrom);
|
||||
}
|
||||
|
||||
$phpunit = $this->phpci->findBinary('phpunit');
|
||||
|
||||
$cmd = $phpunit . ' --tap %s -c "%s" ' . $this->coverage . $this->path;
|
||||
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath);
|
||||
|
||||
if ($this->runFrom) {
|
||||
chdir($curdir);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the PHPUnit tests in a specific directory or array of directories.
|
||||
* @param $directory
|
||||
* @return bool|mixed
|
||||
*/
|
||||
protected function runDir($directory)
|
||||
{
|
||||
if (is_array($directory)) {
|
||||
return $this->recurseArg($directory, [$this, "runDir"]);
|
||||
} else {
|
||||
$curdir = getcwd();
|
||||
chdir($this->phpci->buildPath);
|
||||
|
||||
$phpunit = $this->phpci->findBinary('phpunit');
|
||||
|
||||
$cmd = $phpunit . ' --tap %s "%s"';
|
||||
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $directory);
|
||||
chdir($curdir);
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
* @param $callable
|
||||
* @return bool|mixed
|
||||
*/
|
||||
protected function recurseArg($array, $callable)
|
||||
{
|
||||
$success = true;
|
||||
foreach ($array as $subItem) {
|
||||
$success &= call_user_func($callable, $subItem);
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
96
src/PHPCensor/Plugin/Shell.php
Normal file
96
src/PHPCensor/Plugin/Shell.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Shell Plugin - Allows execute shell commands.
|
||||
* @author Kinn Coelho Julião <kinncj@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Shell implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
protected $args;
|
||||
|
||||
/**
|
||||
* @var string[] $commands The commands to be executed
|
||||
*/
|
||||
protected $commands = [];
|
||||
|
||||
/**
|
||||
* Standard Constructor
|
||||
*
|
||||
* $options['directory'] Output Directory. Default: %BUILDPATH%
|
||||
* $options['filename'] Phar Filename. Default: build.phar
|
||||
* $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
|
||||
* $options['stub'] Stub Content. No Default Value
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
if (isset($options['command'])) {
|
||||
// Keeping this for backwards compatibility, new projects should use interpolation vars.
|
||||
$options['command'] = str_replace("%buildpath%", $this->phpci->buildPath, $options['command']);
|
||||
$this->commands = [$options['command']];
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Support the new syntax:
|
||||
*
|
||||
* shell:
|
||||
* - "cd /www"
|
||||
* - "rm -f file.txt"
|
||||
*/
|
||||
if (is_array($options)) {
|
||||
$this->commands = $options;
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the shell command.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($this->commands as $command) {
|
||||
$command = $this->phpci->interpolate($command);
|
||||
|
||||
if (!$this->phpci->executeCommand($command)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
141
src/PHPCensor/Plugin/SlackNotify.php
Normal file
141
src/PHPCensor/Plugin/SlackNotify.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
use Maknz\Slack\Client;
|
||||
use Maknz\Slack\Attachment;
|
||||
use Maknz\Slack\AttachmentField;
|
||||
|
||||
/**
|
||||
* Slack Plugin
|
||||
* @author Stephen Ball <phpci@stephen.rebelinblue.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class SlackNotify implements Plugin
|
||||
{
|
||||
private $webHook;
|
||||
private $room;
|
||||
private $username;
|
||||
private $message;
|
||||
private $icon;
|
||||
private $show_status;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
if (is_array($options) && isset($options['webhook_url'])) {
|
||||
$this->webHook = trim($options['webhook_url']);
|
||||
|
||||
if (isset($options['message'])) {
|
||||
$this->message = $options['message'];
|
||||
} else {
|
||||
$this->message = '<%PROJECT_URI%|%PROJECT_TITLE%> - <%BUILD_URI%|Build #%BUILD%> has finished ';
|
||||
$this->message .= 'for commit <%COMMIT_URI%|%SHORT_COMMIT% (%COMMIT_EMAIL%)> ';
|
||||
$this->message .= 'on branch <%BRANCH_URI%|%BRANCH%>';
|
||||
}
|
||||
|
||||
if (isset($options['room'])) {
|
||||
$this->room = $options['room'];
|
||||
} else {
|
||||
$this->room = '#php-censor';
|
||||
}
|
||||
|
||||
if (isset($options['username'])) {
|
||||
$this->username = $options['username'];
|
||||
} else {
|
||||
$this->username = 'PHP Censor';
|
||||
}
|
||||
|
||||
if (isset($options['show_status'])) {
|
||||
$this->show_status = (bool) $options['show_status'];
|
||||
} else {
|
||||
$this->show_status = true;
|
||||
}
|
||||
|
||||
if (isset($options['icon'])) {
|
||||
$this->icon = $options['icon'];
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('Please define the webhook_url for slack_notify plugin!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Slack plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$body = $this->phpci->interpolate($this->message);
|
||||
|
||||
$client = new Client($this->webHook);
|
||||
|
||||
$message = $client->createMessage();
|
||||
|
||||
if (!empty($this->room)) {
|
||||
$message->setChannel($this->room);
|
||||
}
|
||||
|
||||
if (!empty($this->username)) {
|
||||
$message->setUsername($this->username);
|
||||
}
|
||||
|
||||
if (!empty($this->icon)) {
|
||||
$message->setIcon($this->icon);
|
||||
}
|
||||
|
||||
// Include an attachment which shows the status and hide the message
|
||||
if ($this->show_status) {
|
||||
$successfulBuild = $this->build->isSuccessful();
|
||||
|
||||
if ($successfulBuild) {
|
||||
$status = 'Success';
|
||||
$color = 'good';
|
||||
} else {
|
||||
$status = 'Failed';
|
||||
$color = 'danger';
|
||||
}
|
||||
|
||||
// Build up the attachment data
|
||||
$attachment = new Attachment([
|
||||
'fallback' => $body,
|
||||
'pretext' => $body,
|
||||
'color' => $color,
|
||||
'fields' => [
|
||||
new AttachmentField([
|
||||
'title' => 'Status',
|
||||
'value' => $status,
|
||||
'short' => false
|
||||
])
|
||||
]
|
||||
]);
|
||||
|
||||
$message->attach($attachment);
|
||||
|
||||
$body = '';
|
||||
}
|
||||
|
||||
$message->send($body);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
84
src/PHPCensor/Plugin/Sqlite.php
Normal file
84
src/PHPCensor/Plugin/Sqlite.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PDO;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* SQLite Plugin — Provides access to a SQLite database.
|
||||
* @author Corpsee <poisoncorpsee@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Sqlite implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCensor\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $queries = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->queries = $options;
|
||||
$buildSettings = $phpci->getConfig('build_settings');
|
||||
|
||||
if (isset($buildSettings['sqlite'])) {
|
||||
$sql = $buildSettings['sqlite'];
|
||||
$this->path = $sql['path'];
|
||||
}
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to SQLite and runs a specified set of queries.
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
$opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION];
|
||||
$pdo = new PDO('sqlite:' . $this->path, $opts);
|
||||
|
||||
foreach ($this->queries as $query) {
|
||||
$pdo->query($this->phpci->interpolate($query));
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->phpci->logFailure($ex->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
209
src/PHPCensor/Plugin/TechnicalDebt.php
Executable file
209
src/PHPCensor/Plugin/TechnicalDebt.php
Executable file
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
/**
|
||||
* Technical Debt Plugin - Checks for existence of "TODO", "FIXME", etc.
|
||||
*
|
||||
* @author James Inman <james@jamesinman.co.uk>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class TechnicalDebt implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCensor\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $suffixes;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $allowed_errors;
|
||||
|
||||
/**
|
||||
* @var string, based on the assumption the root may not hold the code to be
|
||||
* tested, extends the base path
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var array - paths to ignore
|
||||
*/
|
||||
protected $ignore;
|
||||
|
||||
/**
|
||||
* @var array - terms to search for
|
||||
*/
|
||||
protected $searches;
|
||||
|
||||
|
||||
/**
|
||||
* Check if this plugin can be executed.
|
||||
*
|
||||
* @param $stage
|
||||
* @param Builder $builder
|
||||
* @param Build $build
|
||||
* @return bool
|
||||
*/
|
||||
public static function canExecute($stage, Builder $builder, Build $build)
|
||||
{
|
||||
if ($stage == 'test') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PHPCI\Builder $phpci
|
||||
* @param \PHPCI\Model\Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->suffixes = ['php'];
|
||||
$this->directory = $phpci->buildPath;
|
||||
$this->path = '';
|
||||
$this->ignore = $this->phpci->ignore;
|
||||
$this->allowed_errors = 0;
|
||||
$this->searches = ['TODO', 'FIXME', 'TO DO', 'FIX ME'];
|
||||
|
||||
if (isset($options['searches']) && is_array($options['searches'])) {
|
||||
$this->searches = $options['searches'];
|
||||
}
|
||||
|
||||
if (isset($options['zero_config']) && $options['zero_config']) {
|
||||
$this->allowed_errors = -1;
|
||||
}
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle this plugin's options.
|
||||
* @param $options
|
||||
*/
|
||||
protected function setOptions($options)
|
||||
{
|
||||
foreach (['directory', 'path', 'ignore', 'allowed_errors'] as $key) {
|
||||
if (array_key_exists($key, $options)) {
|
||||
$this->{$key} = $options[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the plugin
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$success = true;
|
||||
$this->phpci->logExecOutput(false);
|
||||
|
||||
$errorCount = $this->getErrorList();
|
||||
|
||||
$this->phpci->log("Found $errorCount instances of " . implode(', ', $this->searches));
|
||||
|
||||
$this->build->storeMeta('technical_debt-warnings', $errorCount);
|
||||
|
||||
if ($this->allowed_errors != -1 && $errorCount > $this->allowed_errors) {
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number and list of errors returned from the search
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorList()
|
||||
{
|
||||
$dirIterator = new \RecursiveDirectoryIterator($this->directory);
|
||||
$iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$files = [];
|
||||
|
||||
$ignores = $this->ignore;
|
||||
$ignores[] = 'phpci.yml';
|
||||
$ignores[] = '.phpci.yml';
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$filePath = $file->getRealPath();
|
||||
$skipFile = false;
|
||||
foreach ($ignores as $ignore) {
|
||||
if (stripos($filePath, $ignore) !== false) {
|
||||
$skipFile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore hidden files, else .git, .sass_cache, etc. all get looped over
|
||||
if (stripos($filePath, DIRECTORY_SEPARATOR . '.') !== false) {
|
||||
$skipFile = true;
|
||||
}
|
||||
|
||||
if ($skipFile == false) {
|
||||
$files[] = $file->getRealPath();
|
||||
}
|
||||
}
|
||||
|
||||
$files = array_filter(array_unique($files));
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
foreach ($this->searches as $search) {
|
||||
$fileContent = file_get_contents($file);
|
||||
$allLines = explode(PHP_EOL, $fileContent);
|
||||
$beforeString = strstr($fileContent, $search, true);
|
||||
|
||||
if (false !== $beforeString) {
|
||||
$lines = explode(PHP_EOL, $beforeString);
|
||||
$lineNumber = count($lines);
|
||||
$content = trim($allLines[$lineNumber - 1]);
|
||||
|
||||
$errorCount++;
|
||||
|
||||
$fileName = str_replace($this->directory, '', $file);
|
||||
|
||||
$this->build->reportError(
|
||||
$this->phpci,
|
||||
'technical_debt',
|
||||
$content,
|
||||
PHPCensor\Model\BuildError::SEVERITY_LOW,
|
||||
$fileName,
|
||||
$lineNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errorCount;
|
||||
}
|
||||
}
|
||||
153
src/PHPCensor/Plugin/Util/ComposerPluginInformation.php
Normal file
153
src/PHPCensor/Plugin/Util/ComposerPluginInformation.php
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Class ComposerPluginInformation
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class ComposerPluginInformation implements InstalledPluginInformation
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $composerPackages;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $pluginInfo = null;
|
||||
|
||||
/**
|
||||
* @param string $filePath The path of installed.json created by composer.
|
||||
* @return ComposerPluginInformation
|
||||
*/
|
||||
public static function buildFromYaml($filePath)
|
||||
{
|
||||
if (file_exists($filePath)) {
|
||||
$installed = json_decode(file_get_contents($filePath));
|
||||
} else {
|
||||
$installed = [];
|
||||
}
|
||||
return new self($installed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass[] $composerPackages This should be the contents of the
|
||||
* installed.json file created by composer
|
||||
*/
|
||||
public function __construct(array $composerPackages)
|
||||
{
|
||||
$this->composerPackages = $composerPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of objects. Each one represents an available plugin
|
||||
* and will have the following properties:
|
||||
* name - The friendly name of the plugin (may be an empty string)
|
||||
* class - The class of the plugin (will include namespace)
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getInstalledPlugins()
|
||||
{
|
||||
$this->loadPluginInfo();
|
||||
return $this->pluginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the class names of plugins that have been
|
||||
* loaded.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPluginClasses()
|
||||
{
|
||||
return array_map(
|
||||
function (Plugin $plugin) {
|
||||
return $plugin->class;
|
||||
},
|
||||
$this->getInstalledPlugins()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of available plugins from the installed composer packages.
|
||||
*/
|
||||
protected function loadPluginInfo()
|
||||
{
|
||||
if ($this->pluginInfo !== null) {
|
||||
return;
|
||||
}
|
||||
$this->pluginInfo = [];
|
||||
foreach ($this->composerPackages as $package) {
|
||||
$this->addPluginsFromPackage($package);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $package
|
||||
*/
|
||||
protected function addPluginsFromPackage($package)
|
||||
{
|
||||
if (isset($package->extra->phpci)) {
|
||||
$phpciData = $package->extra->phpci;
|
||||
|
||||
if (isset($phpciData->pluginNamespace)) {
|
||||
$rootNamespace = $phpciData->pluginNamespace;
|
||||
} else {
|
||||
$rootNamespace = "";
|
||||
}
|
||||
|
||||
if (is_array($phpciData->suppliedPlugins)) {
|
||||
$this->addPlugins(
|
||||
$phpciData->suppliedPlugins,
|
||||
$package->name,
|
||||
$rootNamespace
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass[] $plugins
|
||||
* @param string $sourcePackageName
|
||||
* @param string $rootNamespace
|
||||
*/
|
||||
protected function addPlugins(
|
||||
array $plugins,
|
||||
$sourcePackageName,
|
||||
$rootNamespace = ""
|
||||
) {
|
||||
foreach ($plugins as $plugin) {
|
||||
if (!isset($plugin->class)) {
|
||||
continue;
|
||||
}
|
||||
$this->addPlugin($plugin, $sourcePackageName, $rootNamespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $plugin
|
||||
* @param string $sourcePackageName
|
||||
* @param string $rootNamespace
|
||||
*/
|
||||
protected function addPlugin(
|
||||
$plugin,
|
||||
$sourcePackageName,
|
||||
$rootNamespace = ""
|
||||
) {
|
||||
$newPlugin = clone $plugin;
|
||||
|
||||
$newPlugin->class = $rootNamespace . $newPlugin->class;
|
||||
|
||||
if (!isset($newPlugin->name)) {
|
||||
$newPlugin->name = "";
|
||||
}
|
||||
|
||||
$newPlugin->source = $sourcePackageName;
|
||||
|
||||
$this->pluginInfo[] = $newPlugin;
|
||||
}
|
||||
}
|
||||
254
src/PHPCensor/Plugin/Util/Executor.php
Normal file
254
src/PHPCensor/Plugin/Util/Executor.php
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
use b8\Store\Factory as StoreFactory;
|
||||
use Exception;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use PHPCensor\Logging\BuildLogger;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Store\BuildStore;
|
||||
|
||||
/**
|
||||
* Plugin Executor - Runs the configured plugins for a given build stage.
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class Executor
|
||||
{
|
||||
/**
|
||||
* @var BuildLogger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $pluginFactory;
|
||||
|
||||
/**
|
||||
* @var BuildStore
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* @param Factory $pluginFactory
|
||||
* @param BuildLogger $logger
|
||||
*/
|
||||
public function __construct(Factory $pluginFactory, BuildLogger $logger, BuildStore $store = null)
|
||||
{
|
||||
$this->pluginFactory = $pluginFactory;
|
||||
$this->logger = $logger;
|
||||
$this->store = $store ?: StoreFactory::getStore('Build');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a the appropriate set of plugins for a given build stage.
|
||||
* @param array $config PHPCI configuration
|
||||
* @param string $stage
|
||||
* @return bool
|
||||
*/
|
||||
public function executePlugins(&$config, $stage)
|
||||
{
|
||||
$success = true;
|
||||
$pluginsToExecute = [];
|
||||
|
||||
// If we have global plugins to execute for this stage, add them to the list to be executed:
|
||||
if (array_key_exists($stage, $config) && is_array($config[$stage])) {
|
||||
$pluginsToExecute[] = $config[$stage];
|
||||
}
|
||||
|
||||
$pluginsToExecute = $this->getBranchSpecificPlugins($config, $stage, $pluginsToExecute);
|
||||
|
||||
foreach ($pluginsToExecute as $pluginSet) {
|
||||
if (!$this->doExecutePlugins($pluginSet, $stage)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the config for any plugins specific to the branch we're currently building.
|
||||
* @param $config
|
||||
* @param $stage
|
||||
* @param $pluginsToExecute
|
||||
* @return array
|
||||
*/
|
||||
protected function getBranchSpecificPlugins(&$config, $stage, $pluginsToExecute)
|
||||
{
|
||||
/** @var \PHPCI\Model\Build $build */
|
||||
$build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build');
|
||||
$branch = $build->getBranch();
|
||||
|
||||
// If we don't have any branch-specific plugins:
|
||||
if (!isset($config['branch-' . $branch][$stage]) || !is_array($config['branch-' . $branch][$stage])) {
|
||||
return $pluginsToExecute;
|
||||
}
|
||||
|
||||
// If we have branch-specific plugins to execute, add them to the list to be executed:
|
||||
$branchConfig = $config['branch-' . $branch];
|
||||
$plugins = $branchConfig[$stage];
|
||||
|
||||
$runOption = 'after';
|
||||
|
||||
if (!empty($branchConfig['run-option'])) {
|
||||
$runOption = $branchConfig['run-option'];
|
||||
}
|
||||
|
||||
switch ($runOption) {
|
||||
// Replace standard plugin set for this stage with just the branch-specific ones:
|
||||
case 'replace':
|
||||
$pluginsToExecute = [];
|
||||
$pluginsToExecute[] = $plugins;
|
||||
break;
|
||||
|
||||
// Run branch-specific plugins before standard plugins:
|
||||
case 'before':
|
||||
array_unshift($pluginsToExecute, $plugins);
|
||||
break;
|
||||
|
||||
// Run branch-specific plugins after standard plugins:
|
||||
case 'after':
|
||||
array_push($pluginsToExecute, $plugins);
|
||||
break;
|
||||
|
||||
default:
|
||||
array_push($pluginsToExecute, $plugins);
|
||||
break;
|
||||
}
|
||||
|
||||
return $pluginsToExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list of plugins found for a given testing stage.
|
||||
* @param $plugins
|
||||
* @param $stage
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function doExecutePlugins(&$plugins, $stage)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($plugins as $plugin => $options) {
|
||||
$this->logger->log("\n" . Lang::get('running_plugin', Lang::get($plugin)) . ' (' . Lang::get('stage') . ': ' . Lang::get('stage_' . $stage) . ')');
|
||||
|
||||
$this->setPluginStatus($stage, $plugin, Build::STATUS_RUNNING);
|
||||
|
||||
// Try and execute it
|
||||
if ($this->executePlugin($plugin, $options)) {
|
||||
// Execution was successful
|
||||
$this->logger->logSuccess(Lang::get('plugin_success'));
|
||||
$this->setPluginStatus($stage, $plugin, Build::STATUS_SUCCESS);
|
||||
} else {
|
||||
// Execution failed
|
||||
$this->setPluginStatus($stage, $plugin, Build::STATUS_FAILED);
|
||||
|
||||
if ($stage === 'setup') {
|
||||
$this->logger->logFailure(Lang::get('plugin_failed'));
|
||||
// If we're in the "setup" stage, execution should not continue after
|
||||
// a plugin has failed:
|
||||
throw new Exception('Plugin failed: ' . $plugin);
|
||||
} else {
|
||||
// If we're in the "test" stage and the plugin is not allowed to fail,
|
||||
// then mark the build as failed:
|
||||
if (empty($options['allow_failures']) && $stage === 'test') {
|
||||
$this->logger->logFailure(Lang::get('plugin_failed'));
|
||||
$success = false;
|
||||
} else {
|
||||
$this->logger->logFailure(Lang::get('plugin_failed') . ' (' . Lang::get('failed_allowed') . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given plugin, with options and returns the result.
|
||||
*/
|
||||
public function executePlugin($plugin, $options)
|
||||
{
|
||||
// Any plugin name without a namespace separator is a PHPCI built in plugin
|
||||
// if not we assume it's a fully name-spaced class name that implements the plugin interface.
|
||||
// If not the factory will throw an exception.
|
||||
if (strpos($plugin, '\\') === false) {
|
||||
$class = str_replace('_', ' ', $plugin);
|
||||
$class = ucwords($class);
|
||||
$class = 'PHPCensor\\Plugin\\' . str_replace(' ', '', $class);
|
||||
} else {
|
||||
$class = $plugin;
|
||||
}
|
||||
|
||||
if (!class_exists($class)) {
|
||||
$this->logger->logFailure(Lang::get('plugin_missing', $plugin));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Build and run it
|
||||
$obj = $this->pluginFactory->buildPlugin($class, $options);
|
||||
|
||||
return $obj->execute();
|
||||
} catch (\Exception $ex) {
|
||||
$this->logger->logFailure(Lang::get('exception') . $ex->getMessage(), $ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of a plugin for a given stage.
|
||||
*
|
||||
* @param string $stage The builder stage.
|
||||
* @param string $plugin The plugin name.
|
||||
* @param int $status The new status.
|
||||
*/
|
||||
protected function setPluginStatus($stage, $plugin, $status)
|
||||
{
|
||||
$summary = $this->getBuildSummary();
|
||||
|
||||
if (!isset($summary[$stage][$plugin])) {
|
||||
$summary[$stage][$plugin] = [];
|
||||
}
|
||||
|
||||
$summary[$stage][$plugin]['status'] = $status;
|
||||
|
||||
if ($status === Build::STATUS_RUNNING) {
|
||||
$summary[$stage][$plugin]['started'] = time();
|
||||
} elseif ($status >= Build::STATUS_SUCCESS) {
|
||||
$summary[$stage][$plugin]['ended'] = time();
|
||||
}
|
||||
|
||||
$this->setBuildSummary($summary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the summary data of the current build.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBuildSummary()
|
||||
{
|
||||
/** @var Build $build */
|
||||
$build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build');
|
||||
$metas = $this->store->getMeta('plugin-summary', $build->getProjectId(), $build->getId());
|
||||
return isset($metas[0]['meta_value']) ? $metas[0]['meta_value'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the summary data of the current build.
|
||||
*
|
||||
* @param array $summary
|
||||
*/
|
||||
private function setBuildSummary($summary)
|
||||
{
|
||||
/** @var Build $build */
|
||||
$build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build');
|
||||
$this->store->setMeta($build->getProjectId(), $build->getId(), 'plugin-summary', json_encode($summary));
|
||||
}
|
||||
}
|
||||
216
src/PHPCensor/Plugin/Util/Factory.php
Normal file
216
src/PHPCensor/Plugin/Util/Factory.php
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
use Pimple\Container;
|
||||
|
||||
/**
|
||||
* Plugin Factory - Loads Plugins and passes required dependencies.
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
const TYPE_ARRAY = "array";
|
||||
const TYPE_CALLABLE = "callable";
|
||||
const INTERFACE_PHPCI_PLUGIN = '\PHPCensor\Plugin';
|
||||
|
||||
private $currentPluginOptions;
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(Container $container = null)
|
||||
{
|
||||
if ($container) {
|
||||
$this->container = $container;
|
||||
} else {
|
||||
$this->container = new Container();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to get a function from the file path specified. If the
|
||||
* file returns a function then $this will be passed to it.
|
||||
* This enables the config file to call any public methods.
|
||||
*
|
||||
* @param $configPath
|
||||
* @return bool - true if the function exists else false.
|
||||
*/
|
||||
public function addConfigFromFile($configPath)
|
||||
{
|
||||
// The file is expected to return a function which can
|
||||
// act on the pluginFactory to register any resources needed.
|
||||
if (file_exists($configPath)) {
|
||||
$configFunction = require($configPath);
|
||||
if (is_callable($configFunction)) {
|
||||
$configFunction($this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get most recently used factory options.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastOptions()
|
||||
{
|
||||
return $this->currentPluginOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instance of plugin of class $className. $options will
|
||||
* be passed along with any resources registered with the factory.
|
||||
*
|
||||
* @param $className
|
||||
* @param array|null $options
|
||||
* @throws \InvalidArgumentException if $className doesn't represent a valid plugin
|
||||
* @return \PHPCI\Plugin
|
||||
*/
|
||||
public function buildPlugin($className, $options = [])
|
||||
{
|
||||
$this->currentPluginOptions = $options;
|
||||
|
||||
$reflectedPlugin = new \ReflectionClass($className);
|
||||
|
||||
if (!$reflectedPlugin->implementsInterface(self::INTERFACE_PHPCI_PLUGIN)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Requested class must implement " . self:: INTERFACE_PHPCI_PLUGIN
|
||||
);
|
||||
}
|
||||
|
||||
$constructor = $reflectedPlugin->getConstructor();
|
||||
|
||||
if ($constructor) {
|
||||
$argsToUse = [];
|
||||
foreach ($constructor->getParameters() as $param) {
|
||||
if ('options' === $param->getName()) {
|
||||
$argsToUse[] = $options;
|
||||
} else {
|
||||
$argsToUse = $this->addArgFromParam($argsToUse, $param);
|
||||
}
|
||||
}
|
||||
$plugin = $reflectedPlugin->newInstanceArgs($argsToUse);
|
||||
} else {
|
||||
$plugin = $reflectedPlugin->newInstance();
|
||||
}
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $loader
|
||||
* @param string|null $name
|
||||
* @param string|null $type
|
||||
* @throws \InvalidArgumentException
|
||||
* @internal param mixed $resource
|
||||
*/
|
||||
public function registerResource(
|
||||
$loader,
|
||||
$name = null,
|
||||
$type = null
|
||||
) {
|
||||
if ($name === null && $type === null) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Type or Name must be specified"
|
||||
);
|
||||
}
|
||||
|
||||
if (!($loader instanceof \Closure)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'$loader is expected to be a function'
|
||||
);
|
||||
}
|
||||
|
||||
$resourceID = $this->getInternalID($type, $name);
|
||||
|
||||
$this->container[$resourceID] = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an internal resource ID.
|
||||
* @param null $type
|
||||
* @param null $name
|
||||
* @return string
|
||||
*/
|
||||
private function getInternalID($type = null, $name = null)
|
||||
{
|
||||
$type = $type ? : "";
|
||||
$name = $name ? : "";
|
||||
return $type . "-" . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResourceFor($type = null, $name = null)
|
||||
{
|
||||
$fullId = $this->getInternalID($type, $name);
|
||||
if (isset($this->container[$fullId])) {
|
||||
return $this->container[$fullId];
|
||||
}
|
||||
|
||||
$typeOnlyID = $this->getInternalID($type, null);
|
||||
if (isset($this->container[$typeOnlyID])) {
|
||||
return $this->container[$typeOnlyID];
|
||||
}
|
||||
|
||||
$nameOnlyID = $this->getInternalID(null, $name);
|
||||
if (isset($this->container[$nameOnlyID])) {
|
||||
return $this->container[$nameOnlyID];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionParameter $param
|
||||
* @return null|string
|
||||
*/
|
||||
private function getParamType(\ReflectionParameter $param)
|
||||
{
|
||||
$class = $param->getClass();
|
||||
if ($class) {
|
||||
return $class->getName();
|
||||
} elseif ($param->isArray()) {
|
||||
return self::TYPE_ARRAY;
|
||||
} elseif (is_callable($param)) {
|
||||
return self::TYPE_CALLABLE;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $existingArgs
|
||||
* @param \ReflectionParameter $param
|
||||
* @return array
|
||||
* @throws \DomainException
|
||||
*/
|
||||
private function addArgFromParam($existingArgs, \ReflectionParameter $param)
|
||||
{
|
||||
$name = $param->getName();
|
||||
$type = $this->getParamType($param);
|
||||
$arg = $this->getResourceFor($type, $name);
|
||||
|
||||
if ($arg !== null) {
|
||||
$existingArgs[] = $arg;
|
||||
} elseif ($arg === null && $param->isOptional()) {
|
||||
$existingArgs[] = $param->getDefaultValue();
|
||||
} else {
|
||||
throw new \DomainException(
|
||||
"Unsatisfied dependency: " . $param->getName()
|
||||
);
|
||||
}
|
||||
|
||||
return $existingArgs;
|
||||
}
|
||||
}
|
||||
137
src/PHPCensor/Plugin/Util/FilesPluginInformation.php
Normal file
137
src/PHPCensor/Plugin/Util/FilesPluginInformation.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Class FilesPluginInformation
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class FilesPluginInformation implements InstalledPluginInformation
|
||||
{
|
||||
|
||||
/**
|
||||
* A collection of all the file path information for
|
||||
* the installed plugins.
|
||||
*
|
||||
* @var \SplFileInfo[]
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Each item in the array contains the information for
|
||||
* a single plugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pluginInfo = null;
|
||||
|
||||
/**
|
||||
* @param $dirPath
|
||||
* @return FilesPluginInformation
|
||||
*/
|
||||
public static function newFromDir($dirPath)
|
||||
{
|
||||
return new self(new \DirectoryIterator($dirPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Iterator $files
|
||||
*/
|
||||
public function __construct(\Iterator $files)
|
||||
{
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of objects. Each one represents an available plugin
|
||||
* and will have the following properties:
|
||||
* name - The friendly name of the plugin (may be an empty string)
|
||||
* class - The class of the plugin (will include namespace)
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getInstalledPlugins()
|
||||
{
|
||||
if ($this->pluginInfo === null) {
|
||||
$this->loadPluginInfo();
|
||||
}
|
||||
|
||||
return $this->pluginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the class names of plugins that have been
|
||||
* loaded.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPluginClasses()
|
||||
{
|
||||
return array_map(
|
||||
function (\stdClass $plugin) {
|
||||
return $plugin->class;
|
||||
},
|
||||
$this->getInstalledPlugins()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin information from a given list of files.
|
||||
*/
|
||||
protected function loadPluginInfo()
|
||||
{
|
||||
$this->pluginInfo = [];
|
||||
foreach ($this->files as $fileInfo) {
|
||||
if ($fileInfo instanceof \SplFileInfo) {
|
||||
if ($fileInfo->isFile() && $fileInfo->getExtension() == 'php') {
|
||||
$this->addPluginFromFile($fileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plugin to the list from a given file.
|
||||
* @param \SplFileInfo $fileInfo
|
||||
*/
|
||||
protected function addPluginFromFile(\SplFileInfo $fileInfo)
|
||||
{
|
||||
$class = $this->getFullClassFromFile($fileInfo);
|
||||
|
||||
if (!is_null($class)) {
|
||||
$newPlugin = new \stdClass();
|
||||
$newPlugin->class = $class;
|
||||
$newPlugin->source = "core";
|
||||
$parts = explode('\\', $newPlugin->class);
|
||||
$newPlugin->name = end($parts);
|
||||
|
||||
$this->pluginInfo[] = $newPlugin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine plugin class name for a given file.
|
||||
* @param \SplFileInfo $fileInfo
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getFullClassFromFile(\SplFileInfo $fileInfo)
|
||||
{
|
||||
$contents = file_get_contents($fileInfo->getRealPath());
|
||||
$matches = [];
|
||||
|
||||
preg_match('#class +([A-Za-z]+) +implements#i', $contents, $matches);
|
||||
|
||||
if (isset($matches[1])) {
|
||||
$className = $matches[1];
|
||||
|
||||
$matches = [];
|
||||
preg_match('#namespace +([A-Za-z\\\\]+);#i', $contents, $matches);
|
||||
$namespace = $matches[1];
|
||||
|
||||
return $namespace . '\\' . $className;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/PHPCensor/Plugin/Util/InstalledPluginInformation.php
Normal file
26
src/PHPCensor/Plugin/Util/InstalledPluginInformation.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
/**
|
||||
* Interface InstalledPluginInformation
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
interface InstalledPluginInformation
|
||||
{
|
||||
/**
|
||||
* Returns an array of objects. Each one represents an available plugin
|
||||
* and will have the following properties:
|
||||
* name - The friendly name of the plugin (may be an empty string)
|
||||
* class - The class of the plugin (will include namespace)
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getInstalledPlugins();
|
||||
|
||||
/**
|
||||
* Returns an array of all the class names of plugins that have been
|
||||
* loaded.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPluginClasses();
|
||||
}
|
||||
59
src/PHPCensor/Plugin/Util/PluginInformationCollection.php
Normal file
59
src/PHPCensor/Plugin/Util/PluginInformationCollection.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
/**
|
||||
* Class PluginInformationCollection
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class PluginInformationCollection implements InstalledPluginInformation
|
||||
{
|
||||
/**
|
||||
* @var InstalledPluginInformation[]
|
||||
*/
|
||||
protected $pluginInformations = [];
|
||||
|
||||
/**
|
||||
* Add a plugin to the collection.
|
||||
* @param InstalledPluginInformation $information
|
||||
*/
|
||||
public function add(InstalledPluginInformation $information)
|
||||
{
|
||||
$this->pluginInformations[] = $information;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of objects. Each one represents an available plugin
|
||||
* and will have the following properties:
|
||||
* name - The friendly name of the plugin (may be an empty string)
|
||||
* class - The class of the plugin (will include namespace)
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getInstalledPlugins()
|
||||
{
|
||||
$arr = [];
|
||||
|
||||
foreach ($this->pluginInformations as $single) {
|
||||
$arr = array_merge($arr, $single->getInstalledPlugins());
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the class names of plugins that have been
|
||||
* loaded.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPluginClasses()
|
||||
{
|
||||
$arr = [];
|
||||
|
||||
foreach ($this->pluginInformations as $single) {
|
||||
$arr = array_merge($arr, $single->getPluginClasses());
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
286
src/PHPCensor/Plugin/Util/TapParser.php
Normal file
286
src/PHPCensor/Plugin/Util/TapParser.php
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util;
|
||||
|
||||
use Exception;
|
||||
use PHPCensor\Helper\Lang;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Processes TAP format strings into usable test result data.
|
||||
* @package PHPCI\Plugin\Util
|
||||
*/
|
||||
class TapParser
|
||||
{
|
||||
const TEST_COUNTS_PATTERN = '/^\d+\.\.(\d+)/';
|
||||
const TEST_LINE_PATTERN = '/^(ok|not ok)(?:\s+\d+)?(?:\s+\-)?\s*(.*?)(?:\s*#\s*(skip|todo)\s*(.*))?\s*$/i';
|
||||
const TEST_YAML_START = '/^(\s*)---/';
|
||||
const TEST_DIAGNOSTIC = '/^#/';
|
||||
const TEST_COVERAGE = '/^Generating/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tapString;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $failures = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $lines;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $lineNumber;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $testCount;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $results;
|
||||
|
||||
/**
|
||||
* Create a new TAP parser for a given string.
|
||||
* @param string $tapString The TAP format string to be parsed.
|
||||
*/
|
||||
public function __construct($tapString)
|
||||
{
|
||||
$this->tapString = trim($tapString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a given TAP format string and return an array of tests and their status.
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
// Split up the TAP string into an array of lines, then
|
||||
// trim all of the lines so there's no leading or trailing whitespace.
|
||||
$this->lines = array_map('rtrim', explode("\n", $this->tapString));
|
||||
$this->lineNumber = 0;
|
||||
|
||||
$this->testCount = false;
|
||||
$this->results = [];
|
||||
|
||||
$header = $this->findTapLog();
|
||||
|
||||
$line = $this->nextLine();
|
||||
if ($line === $header) {
|
||||
throw new Exception("Duplicated TAP log, please check the configuration.");
|
||||
}
|
||||
|
||||
while ($line !== false && ($this->testCount === false || count($this->results) < $this->testCount)) {
|
||||
$this->parseLine($line);
|
||||
$line = $this->nextLine();
|
||||
}
|
||||
|
||||
if (false !== $this->testCount && count($this->results) !== $this->testCount) {
|
||||
throw new Exception(Lang::get('tap_error'));
|
||||
}
|
||||
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/** Looks for the start of the TAP log in the string.
|
||||
*
|
||||
* @return string The TAP header line.
|
||||
*
|
||||
* @throws Exception if no TAP log is found or versions mismatch.
|
||||
*/
|
||||
protected function findTapLog()
|
||||
{
|
||||
// Look for the beginning of the TAP output
|
||||
do {
|
||||
$header = $this->nextLine();
|
||||
} while ($header !== false && substr($header, 0, 12) !== 'TAP version ');
|
||||
|
||||
//
|
||||
if ($header === false) {
|
||||
throw new Exception('No TAP log found, please check the configuration.');
|
||||
} elseif ($header !== 'TAP version 13') {
|
||||
throw new Exception(Lang::get('tap_version'));
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/** Fetch the next line.
|
||||
*
|
||||
* @return string|false The next line or false if the end has been reached.
|
||||
*/
|
||||
protected function nextLine()
|
||||
{
|
||||
if ($this->lineNumber < count($this->lines)) {
|
||||
return $this->lines[$this->lineNumber++];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $line
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function testLine($line)
|
||||
{
|
||||
if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
|
||||
$this->results[] = $this->processTestLine(
|
||||
$matches[1],
|
||||
isset($matches[2]) ? $matches[2] : '',
|
||||
isset($matches[3]) ? $matches[3] : null,
|
||||
isset($matches[4]) ? $matches[4] : null
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $line
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function yamlLine($line)
|
||||
{
|
||||
if (preg_match(self::TEST_YAML_START, $line, $matches)) {
|
||||
$diagnostic = $this->processYamlBlock($matches[1]);
|
||||
$test = array_pop($this->results);
|
||||
if (isset($test['message'], $diagnostic['message'])) {
|
||||
$test['message'] .= PHP_EOL . $diagnostic['message'];
|
||||
unset($diagnostic['message']);
|
||||
}
|
||||
$this->results[] = array_replace($test, $diagnostic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Parse a single line.
|
||||
*
|
||||
* @param string $line
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseLine($line)
|
||||
{
|
||||
if (preg_match(self::TEST_DIAGNOSTIC, $line) || preg_match(self::TEST_COVERAGE, $line) || !$line) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match(self::TEST_COUNTS_PATTERN, $line, $matches)) {
|
||||
$this->testCount = intval($matches[1]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->testLine($line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->yamlLine($line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception(sprintf('Incorrect TAP data, line %d: %s', $this->lineNumber, $line));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an individual test line.
|
||||
*
|
||||
* @param string $result
|
||||
* @param string $message
|
||||
* @param string $directive
|
||||
* @param string $reason
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function processTestLine($result, $message, $directive, $reason)
|
||||
{
|
||||
$test = [
|
||||
'pass' => true,
|
||||
'message' => $message,
|
||||
'severity' => 'success',
|
||||
];
|
||||
|
||||
if ($result !== 'ok') {
|
||||
$test['pass'] = false;
|
||||
$test['severity'] = substr($message, 0, 6) === 'Error:' ? 'error' : 'fail';
|
||||
$this->failures++;
|
||||
}
|
||||
|
||||
if ($directive) {
|
||||
$test = $this->processDirective($test, $directive, $reason);
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/** Process an indented Yaml block.
|
||||
*
|
||||
* @param string $indent The block indentation to ignore.
|
||||
*
|
||||
* @return array The processed Yaml content.
|
||||
*/
|
||||
protected function processYamlBlock($indent)
|
||||
{
|
||||
$startLine = $this->lineNumber + 1;
|
||||
$endLine = $indent . '...';
|
||||
$yamlLines = [];
|
||||
|
||||
do {
|
||||
$line = $this->nextLine();
|
||||
|
||||
if ($line === false) {
|
||||
throw new Exception(Lang::get('tap_error_endless_yaml', $startLine));
|
||||
} elseif ($line === $endLine) {
|
||||
break;
|
||||
}
|
||||
|
||||
$yamlLines[] = substr($line, strlen($indent));
|
||||
} while (true);
|
||||
|
||||
return Yaml::parse(join("\n", $yamlLines));
|
||||
}
|
||||
|
||||
/** Process a TAP directive
|
||||
*
|
||||
* @param array $test
|
||||
* @param string $directive
|
||||
* @param string $reason
|
||||
* @return array
|
||||
*/
|
||||
protected function processDirective($test, $directive, $reason)
|
||||
{
|
||||
$test['severity'] = strtolower($directive) === 'skip' ? 'skipped' : 'todo';
|
||||
|
||||
if (!empty($reason)) {
|
||||
if (!empty($test['message'])) {
|
||||
$test['message'] .= ', '.$test['severity'].': ';
|
||||
}
|
||||
$test['message'] .= $reason;
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of failures from the current TAP file.
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalFailures()
|
||||
{
|
||||
return $this->failures;
|
||||
}
|
||||
}
|
||||
113
src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php
Normal file
113
src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util\TestResultParsers;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
|
||||
/**
|
||||
* Class Codeception
|
||||
*
|
||||
* @author Adam Cooper <adam@networkpie.co.uk>
|
||||
* @package PHPCI\Plugin\Util\TestResultParsers
|
||||
*/
|
||||
class Codeception implements ParserInterface
|
||||
{
|
||||
protected $phpci;
|
||||
protected $resultsXml;
|
||||
|
||||
protected $results;
|
||||
|
||||
protected $totalTests;
|
||||
protected $totalTimeTaken;
|
||||
protected $totalFailures;
|
||||
protected $totalErrors;
|
||||
|
||||
/**
|
||||
* @param Builder $phpci
|
||||
* @param $resultsXml
|
||||
*/
|
||||
public function __construct(Builder $phpci, $resultsXml)
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->resultsXml = $resultsXml;
|
||||
$this->totalTests = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array An array of key/value pairs for storage in the plugins result metadata
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
$rtn = [];
|
||||
$this->results = new \SimpleXMLElement($this->resultsXml);
|
||||
|
||||
// calculate total results
|
||||
foreach ($this->results->testsuite as $test_suite) {
|
||||
$this->totalTests += (int)$test_suite['tests'];
|
||||
$this->totalTimeTaken += (float)$test_suite['time'];
|
||||
$this->totalFailures += (int)$test_suite['failures'];
|
||||
$this->totalErrors += (int)$test_suite['errors'];
|
||||
|
||||
foreach ($test_suite->testcase as $test_case) {
|
||||
$test_result = [
|
||||
'suite' => (string)$test_suite['name'],
|
||||
'file' => str_replace($this->phpci->buildPath, '/', (string) $test_case['file']),
|
||||
'name' => (string)$test_case['name'],
|
||||
'feature' => (string)$test_case['feature'],
|
||||
'assertions' => (int)$test_case['assertions'],
|
||||
'time' => (float)$test_case['time']
|
||||
];
|
||||
|
||||
if (isset($test_case['class'])) {
|
||||
$test_result['class'] = (string) $test_case['class'];
|
||||
}
|
||||
|
||||
// PHPUnit testcases does not have feature field. Use class::method instead
|
||||
if (!$test_result['feature']) {
|
||||
$test_result['feature'] = sprintf('%s::%s', $test_result['class'], $test_result['name']);
|
||||
}
|
||||
|
||||
if (isset($test_case->failure) || isset($test_case->error)) {
|
||||
$test_result['pass'] = false;
|
||||
$test_result['message'] = isset($test_case->failure) ? (string)$test_case->failure : (string)$test_case->error;
|
||||
} else {
|
||||
$test_result['pass'] = true;
|
||||
}
|
||||
|
||||
$rtn[] = $test_result;
|
||||
}
|
||||
}
|
||||
|
||||
return $rtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of tests performed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalTests()
|
||||
{
|
||||
return $this->totalTests;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time take to complete all tests
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTotalTimeTaken()
|
||||
{
|
||||
return $this->totalTimeTaken;
|
||||
}
|
||||
|
||||
/**
|
||||
* A count of the test failures
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTotalFailures()
|
||||
{
|
||||
return $this->totalFailures + $this->totalErrors;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor\Plugin\Util\TestResultParsers;
|
||||
|
||||
interface ParserInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array An array of key/value pairs for storage in the plugins result metadata
|
||||
*/
|
||||
public function parse();
|
||||
|
||||
public function getTotalTests();
|
||||
public function getTotalTimeTaken();
|
||||
public function getTotalFailures();
|
||||
}
|
||||
71
src/PHPCensor/Plugin/Wipe.php
Normal file
71
src/PHPCensor/Plugin/Wipe.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* Wipe Plugin - Wipes a folder
|
||||
* @author Claus Due <claus@namelesscoder.net>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Wipe implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Model\Build
|
||||
*/
|
||||
protected $build;
|
||||
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* Set up the plugin, configure options, etc.
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$path = $phpci->buildPath;
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
$this->directory = isset($options['directory']) ? $this->phpci->interpolate($options['directory']) : $path;
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipes a directory's contents
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$build = $this->phpci->buildPath;
|
||||
|
||||
if ($this->directory == $build || empty($this->directory)) {
|
||||
return true;
|
||||
}
|
||||
if (is_dir($this->directory)) {
|
||||
$cmd = 'rm -Rf "%s"';
|
||||
if (IS_WIN) {
|
||||
$cmd = 'rmdir /S /Q "%s"';
|
||||
}
|
||||
return $this->phpci->executeCommand($cmd, $this->directory);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
220
src/PHPCensor/Plugin/Xmpp.php
Normal file
220
src/PHPCensor/Plugin/Xmpp.php
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2014, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link https://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCensor\Plugin;
|
||||
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\Model\Build;
|
||||
use PHPCensor\Plugin;
|
||||
|
||||
/**
|
||||
* XMPP Notification - Send notification for successful or failure build
|
||||
* @author Alexandre Russo <dev.github@ange7.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class XMPP implements Plugin
|
||||
{
|
||||
protected $directory;
|
||||
protected $phpci;
|
||||
protected $build;
|
||||
|
||||
/**
|
||||
* @var string, username of sender account xmpp
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* @var string, alias server of sender account xmpp
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* @var string, password of sender account xmpp
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* @var string, alias for sender
|
||||
*/
|
||||
protected $alias;
|
||||
|
||||
/**
|
||||
* @var string, use tls
|
||||
*/
|
||||
protected $tls;
|
||||
|
||||
/**
|
||||
* @var array, list of recipients xmpp accounts
|
||||
*/
|
||||
protected $recipients;
|
||||
|
||||
/**
|
||||
* @var string, mask to format date
|
||||
*/
|
||||
protected $date_format;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Builder $phpci
|
||||
* @param Build $build
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Builder $phpci, Build $build, array $options = [])
|
||||
{
|
||||
$this->phpci = $phpci;
|
||||
$this->build = $build;
|
||||
|
||||
$this->username = '';
|
||||
$this->password = '';
|
||||
$this->server = '';
|
||||
$this->alias = '';
|
||||
$this->recipients = [];
|
||||
$this->tls = false;
|
||||
$this->date_format = '%c';
|
||||
|
||||
/*
|
||||
* Set recipients list
|
||||
*/
|
||||
if (!empty($options['recipients'])) {
|
||||
if (is_string($options['recipients'])) {
|
||||
$this->recipients = [$options['recipients']];
|
||||
} elseif (is_array($options['recipients'])) {
|
||||
$this->recipients = $options['recipients'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->phpci->logDebug('Plugin options: ' . json_encode($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options configuration for plugin
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
protected function setOptions($options)
|
||||
{
|
||||
foreach (['username', 'password', 'alias', 'tls', 'server', 'date_format'] as $key) {
|
||||
if (array_key_exists($key, $options)) {
|
||||
$this->{$key} = $options[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config format for sendxmpp config file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigFormat()
|
||||
{
|
||||
$conf = $this->username;
|
||||
if (!empty($this->server)) {
|
||||
$conf .= ';'.$this->server;
|
||||
}
|
||||
|
||||
$conf .= ' '.$this->password;
|
||||
|
||||
if (!empty($this->alias)) {
|
||||
$conf .= ' '.$this->alias;
|
||||
}
|
||||
|
||||
return $conf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find config file for sendxmpp binary (default is .sendxmpprc)
|
||||
*/
|
||||
public function findConfigFile()
|
||||
{
|
||||
if (file_exists($this->phpci->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc')) {
|
||||
if (md5(file_get_contents($this->phpci->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc'))
|
||||
!== md5($this->getConfigFormat())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification message.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$sendxmpp = $this->phpci->findBinary('sendxmpp');
|
||||
|
||||
/*
|
||||
* Without recipients we can't send notification
|
||||
*/
|
||||
if (count($this->recipients) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to build conf file
|
||||
*/
|
||||
$config_file = $this->phpci->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc';
|
||||
if (is_null($this->findConfigFile())) {
|
||||
file_put_contents($config_file, $this->getConfigFormat());
|
||||
chmod($config_file, 0600);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enabled ssl for connection
|
||||
*/
|
||||
$tls = '';
|
||||
if ($this->tls) {
|
||||
$tls = ' -t';
|
||||
}
|
||||
|
||||
$message_file = $this->phpci->buildPath . DIRECTORY_SEPARATOR . uniqid('xmppmessage');
|
||||
if ($this->buildMessage($message_file) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send XMPP notification for all recipients
|
||||
*/
|
||||
$cmd = $sendxmpp . "%s -f %s -m %s %s";
|
||||
$recipients = implode(' ', $this->recipients);
|
||||
|
||||
$success = $this->phpci->executeCommand($cmd, $tls, $config_file, $message_file, $recipients);
|
||||
|
||||
print $this->phpci->getLastOutput();
|
||||
|
||||
/*
|
||||
* Remove temp message file
|
||||
*/
|
||||
$this->phpci->executeCommand("rm -rf ".$message_file);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $message_file
|
||||
* @return int
|
||||
*/
|
||||
protected function buildMessage($message_file)
|
||||
{
|
||||
if ($this->build->isSuccessful()) {
|
||||
$message = "✔ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." successful";
|
||||
} else {
|
||||
$message = "✘ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." failure";
|
||||
}
|
||||
|
||||
$message .= ' ('.strftime($this->date_format).')';
|
||||
|
||||
return file_put_contents($message_file, $message);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue