diff --git a/PHPCI/BuildLogger.php b/PHPCI/BuildLogger.php new file mode 100644 index 00000000..ccb6288a --- /dev/null +++ b/PHPCI/BuildLogger.php @@ -0,0 +1,31 @@ + */ -class Builder implements LoggerAwareInterface +class Builder implements LoggerAwareInterface, BuildLogger { /** * @var string @@ -92,9 +92,9 @@ class Builder implements LoggerAwareInterface public $quiet = false; /** - * @var \PHPCI\Plugin\Util\Factory + * @var \PHPCI\Plugin\Util\Executor */ - protected $pluginFactory; + protected $pluginExecutor; /** * Set up the builder. @@ -108,7 +108,7 @@ class Builder implements LoggerAwareInterface } $this->build = $build; $this->store = Store\Factory::getStore('Build'); - $this->setupPluginFactory($build); + $this->pluginExecutor = new Plugin\Util\Executor($this->buildPluginFactory($build), $this); } /** @@ -159,36 +159,39 @@ class Builder implements LoggerAwareInterface public function execute() { // Update the build in the database, ping any external services. - $this->build->setStatus(1); + $this->build->setStatus(Build::STATUS_RUNNING); $this->build->setStarted(new \DateTime()); $this->store->save($this->build); $this->build->sendStatusPostback(); + $this->success = true; - try { - // Set up the build: - $this->setupBuild(); + // Set up the build: + $this->setupBuild(); - // Run the core plugin stages: - foreach (array('setup', 'test', 'complete') as $stage) { - $this->executePlugins($stage); - } + // Run the core plugin stages: + foreach (array('setup', 'test') as $stage) { + $this->success &= $this->pluginExecutor->executePlugins($this->config, $stage); + } - // Failed build? Execute failure plugins and then mark the build as failed. - if (!$this->success) { - $this->executePlugins('failure'); - throw new \Exception('BUILD FAILED!'); - } + // Set the status so this can be used by complete, success and failure + // stages. + if ($this->success) { + $this->build->setStatus(Build::STATUS_SUCCESS); + } + else { + $this->build->setStatus(Build::STATUS_FAILED); + } - // If we got this far, the build was successful! - if ($this->success) { - $this->build->setStatus(2); - $this->executePlugins('success'); - $this->logSuccess('BUILD SUCCESSFUL!'); - } + // Complete stage plugins are always run + $this->pluginExecutor->executePlugins($this->config, 'complete'); - } catch (\Exception $ex) { - $this->logFailure($ex->getMessage(), $ex); - $this->build->setStatus(3); + if ($this->success) { + $this->pluginExecutor->executePlugins($this->config, 'success'); + $this->logSuccess('BUILD SUCCESSFUL!'); + } + else { + $this->pluginExecutor->executePlugins($this->config, 'failure'); + $this->logFailure("BUILD FAILURE"); } // Clean up: @@ -372,79 +375,6 @@ class Builder implements LoggerAwareInterface return true; } - /** - * Execute a the appropriate set of plugins for a given build stage. - */ - protected function executePlugins($stage) - { - // Ignore any stages for which we don't have plugins set: - if (!array_key_exists( - $stage, - $this->config - ) || !is_array($this->config[$stage]) - ) { - return; - } - - foreach ($this->config[$stage] as $plugin => $options) { - $this->log('RUNNING PLUGIN: ' . $plugin); - - // Is this plugin allowed to fail? - if ($stage == 'test' && !isset($options['allow_failures'])) { - $options['allow_failures'] = false; - } - - // Try and execute it: - if ($this->executePlugin($plugin, $options)) { - - // Execution was successful: - $this->logSuccess('PLUGIN STATUS: SUCCESS!'); - - } else { - - // If we're in the "test" stage and the plugin is not allowed to fail, - // then mark the build as failed: - if ($stage == 'test' && !$options['allow_failures']) { - $this->success = false; - } - - $this->logFailure('PLUGIN STATUS: FAILED'); - } - } - } - - /** - * Executes a given plugin, with options and returns the result. - */ - protected function executePlugin($plugin, $options) - { - // Figure out the class name and check the plugin exists: - $class = str_replace('_', ' ', $plugin); - $class = ucwords($class); - $class = 'PHPCI\\Plugin\\' . str_replace(' ', '', $class); - - if (!class_exists($class)) { - $this->logFailure('Plugin does not exist: ' . $plugin, $ex); - return false; - } - - $rtn = true; - - // Try running it: - try { - $obj = $this->pluginFactory->buildPlugin($class, $options); - - if (!$obj->execute()) { - $rtn = false; - } - } catch (\Exception $ex) { - $this->logFailure('EXCEPTION: ' . $ex->getMessage(), $ex); - $rtn = false; - } - - return $rtn; - } - /** * Find a binary required by a plugin. * @param $binary @@ -499,12 +429,12 @@ class Builder implements LoggerAwareInterface return $this->logger; } - private function setupPluginFactory(Build $build) + private function buildPluginFactory(Build $build) { - $this->pluginFactory = new Plugin\Util\Factory(); + $pluginFactory = new Plugin\Util\Factory(); $self = $this; - $this->pluginFactory->registerResource( + $pluginFactory->registerResource( function () use($self) { return $self; }, @@ -512,7 +442,7 @@ class Builder implements LoggerAwareInterface 'PHPCI\Builder' ); - $this->pluginFactory->registerResource( + $pluginFactory->registerResource( function () use($build) { return $build; }, @@ -520,7 +450,7 @@ class Builder implements LoggerAwareInterface 'PHPCI\Model\Build' ); - $this->pluginFactory->registerResource( + $pluginFactory->registerResource( function () use ($self) { $factory = new MailerFactory($self->getSystemConfig('phpci')); return $factory->getSwiftMailerFromConfig(); @@ -528,5 +458,7 @@ class Builder implements LoggerAwareInterface null, 'Swift_Mailer' ); + + return $pluginFactory; } } diff --git a/PHPCI/Command/PollCommand.php b/PHPCI/Command/PollCommand.php index af91acec..3918e27d 100644 --- a/PHPCI/Command/PollCommand.php +++ b/PHPCI/Command/PollCommand.php @@ -84,7 +84,7 @@ class PollCommand extends Command $build = new Build(); $build->setProjectId($project->getId()); $build->setCommitId($last_commit); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setBranch($project->getType() === 'hg' ? 'default' : 'master'); $build->setCreated(new \DateTime()); diff --git a/PHPCI/Controller/BitbucketController.php b/PHPCI/Controller/BitbucketController.php index 9ec4bb39..783421a3 100644 --- a/PHPCI/Controller/BitbucketController.php +++ b/PHPCI/Controller/BitbucketController.php @@ -53,7 +53,7 @@ class BitbucketController extends \PHPCI\Controller $build = new Build(); $build->setProjectId($project); $build->setCommitId($commits[$branch]); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setLog(''); $build->setCreated(new \DateTime()); $build->setBranch($branch); diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index 8c183774..c80cb7c8 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -109,7 +109,7 @@ class BuildController extends \PHPCI\Controller $build = new Build(); $build->setProjectId($copy->getProjectId()); $build->setCommitId($copy->getCommitId()); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setBranch($copy->getBranch()); $build->setCreated(new \DateTime()); @@ -129,6 +129,12 @@ class BuildController extends \PHPCI\Controller } $build = $this->buildStore->getById($buildId); + + if (!$build) { + $this->response->setResponseCode(404); + return '404 - Not Found'; + } + $this->buildStore->delete($build); header('Location: '.PHPCI_URL.'project/view/' . $build->getProjectId()); diff --git a/PHPCI/Controller/GitController.php b/PHPCI/Controller/GitController.php index 396ecaa9..b5a6cf87 100644 --- a/PHPCI/Controller/GitController.php +++ b/PHPCI/Controller/GitController.php @@ -47,7 +47,7 @@ class GitController extends \PHPCI\Controller $build->setCommitId($commit); } - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setLog(''); $build->setCreated(new \DateTime()); } catch (\Exception $ex) { diff --git a/PHPCI/Controller/GithubController.php b/PHPCI/Controller/GithubController.php index b616cbb4..4fe99756 100644 --- a/PHPCI/Controller/GithubController.php +++ b/PHPCI/Controller/GithubController.php @@ -48,7 +48,7 @@ class GithubController extends \PHPCI\Controller $build = new Build(); $build->setProjectId($project); $build->setCommitId($payload['after']); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setLog(''); $build->setCreated(new \DateTime()); $build->setBranch(str_replace('refs/heads/', '', $payload['ref'])); diff --git a/PHPCI/Controller/GitlabController.php b/PHPCI/Controller/GitlabController.php index 020b6346..a466f4dd 100644 --- a/PHPCI/Controller/GitlabController.php +++ b/PHPCI/Controller/GitlabController.php @@ -42,7 +42,7 @@ class GitlabController extends \PHPCI\Controller $build = new Build(); $build->setProjectId($project); $build->setCommitId($payload['after']); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setLog(''); $build->setCreated(new \DateTime()); $build->setBranch(str_replace('refs/heads/', '', $payload['ref'])); diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 1a4fc496..7c0b75e7 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -69,7 +69,7 @@ class ProjectController extends \PHPCI\Controller $build = new Build(); $build->setProjectId($projectId); $build->setCommitId('Manual'); - $build->setStatus(0); + $build->setStatus(Build::STATUS_NEW); $build->setBranch($project->getType() === 'hg' ? 'default' : 'master'); $build->setCreated(new \DateTime()); diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index 9fce197d..6f8f544c 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -101,7 +101,10 @@ class SettingsController extends Controller $dumper = new Dumper(); $yaml = $dumper->dump($this->settings); file_put_contents(APPLICATION_PATH . 'PHPCI/config.yml', $yaml); - if(error_get_last()) return error_get_last()['message']; + if (error_get_last()) { + $error_get_last = error_get_last(); + return $error_get_last['message']; + } } protected function getGithubForm() diff --git a/PHPCI/Helper/LoggerConfig.php b/PHPCI/Helper/LoggerConfig.php index 014805f2..de1b2a67 100644 --- a/PHPCI/Helper/LoggerConfig.php +++ b/PHPCI/Helper/LoggerConfig.php @@ -13,18 +13,31 @@ class LoggerConfig { private $config; /** - * The file specified is expected to return an array. Where each key - * is the name of a logger. The value of each key should be an array or - * a function that returns an array of LogHandlers. - * @param $logConfigFilePath + * The filepath is expected to return an array which will be + * passed to the normal constructor. + * + * @param string $filePath + * @return LoggerConfig */ - function __construct($logConfigFilePath) { - if (file_exists($logConfigFilePath)) { - $this->config = require_once($logConfigFilePath); + public static function newFromFile($filePath) + { + if (file_exists($filePath)) { + $configArray = require($filePath); } else { - $this->config = array(); + $configArray = array(); } + return new self($configArray); + } + + /** + * Each key of the array is the name of a logger. The value of + * each key should be an array or a function that returns an + * array of LogHandlers. + * @param array $configArray + */ + function __construct(array $configArray = array()) { + $this->config = $configArray; } /** @@ -33,7 +46,7 @@ class LoggerConfig { * @param $name * @return Logger */ - public function GetFor($name) { + public function getFor($name) { $handlers = $this->getHandlers(self::KEY_AlwaysLoaded); $handlers = array_merge($handlers, $this->getHandlers($name)); return new Logger($name, $handlers); diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index e3e12ecc..c16d3ee5 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -215,11 +215,19 @@ class ProjectBase extends Model /** * Get the value of AccessInformation / access_information. * + * @param string|null $key Key of desired information + * * @return string */ - public function getAccessInformation() + public function getAccessInformation($key = null) { - $rtn = $this->data['access_information']; + if (is_null($key)) { + $rtn = $this->data['access_information']; + } else if (isset($this->data['access_information'][$key])) { + $rtn = $this->data['access_information'][$key]; + } else { + $rtn = null; + } return $rtn; } diff --git a/PHPCI/Model/Build/GitlabBuild.php b/PHPCI/Model/Build/GitlabBuild.php index f3f75ebe..98be17c8 100644 --- a/PHPCI/Model/Build/GitlabBuild.php +++ b/PHPCI/Model/Build/GitlabBuild.php @@ -25,7 +25,7 @@ class GitlabBuild extends RemoteGitBuild */ public function getCommitLink() { - $domain = $this->getProject()->getAccessInformation()["domain"]; + $domain = $this->getProject()->getAccessInformation("domain"); return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); } @@ -34,7 +34,7 @@ class GitlabBuild extends RemoteGitBuild */ public function getBranchLink() { - $domain = $this->getProject()->getAccessInformation()["domain"]; + $domain = $this->getProject()->getAccessInformation("domain"); return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); } @@ -46,8 +46,8 @@ class GitlabBuild extends RemoteGitBuild $key = trim($this->getProject()->getGitKey()); if (!empty($key)) { - $user = $this->getProject()->getAccessInformation()["user"]; - $domain = $this->getProject()->getAccessInformation()["domain"]; + $user = $this->getProject()->getAccessInformation("user"); + $domain = $this->getProject()->getAccessInformation("domain"); return $user . '@' . $domain . ':' . $this->getProject()->getReference() . '.git'; } } diff --git a/PHPCI/Model/Build/LocalBuild.php b/PHPCI/Model/Build/LocalBuild.php index 08d997dd..8e6984c6 100644 --- a/PHPCI/Model/Build/LocalBuild.php +++ b/PHPCI/Model/Build/LocalBuild.php @@ -57,7 +57,7 @@ class LocalBuild extends Build // If it is indeed a bare repository, then extract it into our build path: if ($gitConfig['core']['bare']) { - $builder->executeCommand('mkdir %2$s; git --git-dir="%1$s" archive master | tar -x -C "%2$s"', $reference, $buildPath); + $builder->executeCommand('mkdir %2$s; git --git-dir="%1$s" archive %3$s | tar -x -C "%2$s"', $reference, $buildPath, $this->getBranch()); return true; } diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php index 9b905fb6..fde1b2fa 100755 --- a/PHPCI/Plugin/PhpUnit.php +++ b/PHPCI/Plugin/PhpUnit.php @@ -128,7 +128,7 @@ class PhpUnit implements \PHPCI\Plugin protected function runDir($dirPath) { if (is_array($dirPath)) { - return $this->recurseArg($dirPath, array($this, "runConfigFile")); + return $this->recurseArg($dirPath, array($this, "runDir")); } else { $curdir = getcwd(); chdir($this->phpci->buildPath); diff --git a/PHPCI/Plugin/Shell.php b/PHPCI/Plugin/Shell.php index d86b6e1b..b9949cd3 100644 --- a/PHPCI/Plugin/Shell.php +++ b/PHPCI/Plugin/Shell.php @@ -1,11 +1,11 @@ -* @package PHPCI -* @subpackage Plugins -*/ + * Shell Plugin - Allows execute shell commands. + * @author Kinn Coelho Julião + * @package PHPCI + * @subpackage Plugins + */ class Shell implements \PHPCI\Plugin { protected $args; protected $phpci; /** - * @var string $command The command to be executed + * @var string[] $commands The commands to be executed */ - protected $command; + protected $commands = array(); public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; if (isset($options['command'])) { - $command = $options['command']; - $command = str_replace("%buildpath%", $this->phpci->buildPath, $command); - $this->command = $command; + // Keeping this for backwards compatibility, new projects should use interpolation vars. + $options['command'] = str_replace("%buildpath%", $this->phpci->buildPath, $options['command']); + $this->commands = array($options['command']); + return; + } + + /* + * Support the new syntax: + * + * shell: + * - "cd /www" + * - "rm -f file.txt" + */ + if (is_array($options)) { + $this->commands = $options; } } /** - * Runs the shell command. - */ + * Runs the shell command. + */ public function execute() { if (!defined('ENABLE_SHELL_PLUGIN') || !ENABLE_SHELL_PLUGIN) { throw new \Exception('The shell plugin is not enabled.'); } - - $success = $this->phpci->executeCommand($this->command); - + + $success = true; + + foreach ($this->commands as $command) { + $command = $this->phpci->interpolate($command); + + if (!$this->phpci->executeCommand($command)) { + $success = false; + } + } + return $success; } } diff --git a/PHPCI/Plugin/Util/Executor.php b/PHPCI/Plugin/Util/Executor.php new file mode 100644 index 00000000..5bec1820 --- /dev/null +++ b/PHPCI/Plugin/Util/Executor.php @@ -0,0 +1,108 @@ +pluginFactory = $pluginFactory; + $this->logger = $logger; + } + + /** + * 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; + // Ignore any stages for which we don't have plugins set: + if (!array_key_exists($stage, $config) || !is_array($config[$stage])) { + return $success; + } + + foreach ($config[$stage] as $plugin => $options) { + $this->logger->log('RUNNING PLUGIN: ' . $plugin); + + // Is this plugin allowed to fail? + if ($stage == 'test' && !isset($options['allow_failures'])) { + $options['allow_failures'] = false; + } + + // Try and execute it: + if ($this->executePlugin($plugin, $options)) { + + // Execution was successful: + $this->logger->logSuccess('PLUGIN STATUS: SUCCESS!'); + + } else { + + // If we're in the "test" stage and the plugin is not allowed to fail, + // then mark the build as failed: + if ($stage == 'test' && !$options['allow_failures']) { + $success = false; + } + + $this->logger->logFailure('PLUGIN STATUS: FAILED'); + } + } + + 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 = 'PHPCI\\Plugin\\' . str_replace(' ', '', $class); + } + else { + $class = $plugin; + } + + if (!class_exists($class)) { + $this->logger->logFailure('Plugin does not exist: ' . $plugin); + return false; + } + + $rtn = true; + + // Try running it: + try { + $obj = $this->pluginFactory->buildPlugin($class, $options); + + if (!$obj->execute()) { + $rtn = false; + } + } catch (\Exception $ex) { + $this->logger->logFailure('EXCEPTION: ' . $ex->getMessage(), $ex); + $rtn = false; + } + + return $rtn; + } + +} \ No newline at end of file diff --git a/PHPCI/Plugin/Util/Factory.php b/PHPCI/Plugin/Util/Factory.php index 09cd44cf..0e526023 100644 --- a/PHPCI/Plugin/Util/Factory.php +++ b/PHPCI/Plugin/Util/Factory.php @@ -9,6 +9,8 @@ class Factory { const TYPE_ARRAY = "array"; const TYPE_CALLABLE = "callable"; + const INTERFACE_PHPCI_PLUGIN = '\PHPCI\Plugin'; + private $currentPluginOptions; /** @@ -45,6 +47,7 @@ class Factory { * * @param $className * @param array $options + * @throws \InvalidArgumentException if $className doesn't represent a valid plugin * @return \PHPCI\Plugin */ public function buildPlugin($className, array $options = array()) @@ -53,6 +56,12 @@ class Factory { $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) { diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index e38d8580..09cdd09b 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -73,7 +73,7 @@ switch($build->getStatus()) diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 68a0f26f..5e48573c 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -58,8 +58,10 @@ \ No newline at end of file diff --git a/PHPCI/View/Plugin/index.phtml b/PHPCI/View/Plugin/index.phtml index f3f10e60..f9170032 100644 --- a/PHPCI/View/Plugin/index.phtml +++ b/PHPCI/View/Plugin/index.phtml @@ -1,7 +1,7 @@

Plugins

-

PHPCI cannot automatically install/remove plugins for you, as the shell_exec() - function is disabled. PHPCI will update composer.json for you, but you will need to run Composer manually to make the changes.

+

PHPCI cannot automatically install/remove plugins for you, as either the shell_exec() + function is disabled or PHPCI could not find Composer. PHPCI will update composer.json for you, but you will need to run Composer manually to make the changes.

diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 762e476a..036f9837 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -92,7 +92,9 @@ $(function() { $('#delete-project').on('click', function (e) { e.preventDefault(); - confirmDelete("project/delete/getId(); ?>"); + confirmDelete( + "project/delete/getId(); ?>", "Project" + ).onCloseConfirmed = function () {window.location = '/'}; }); }) diff --git a/PHPCI/View/User/index.phtml b/PHPCI/View/User/index.phtml index bcdcb7f1..86d6c61d 100644 --- a/PHPCI/View/User/index.phtml +++ b/PHPCI/View/User/index.phtml @@ -50,7 +50,7 @@ diff --git a/Tests/PHPCI/Helper/LoggerConfigTest.php b/Tests/PHPCI/Helper/LoggerConfigTest.php new file mode 100644 index 00000000..95ea3d62 --- /dev/null +++ b/Tests/PHPCI/Helper/LoggerConfigTest.php @@ -0,0 +1,75 @@ +getFor("something"); + $this->assertInstanceOf('\Psr\Log\LoggerInterface', $logger); + } + + public function testGetFor_ReturnsMonologInstance() + { + $config = new LoggerConfig(array()); + $logger = $config->getFor("something"); + $this->assertInstanceOf('\Monolog\Logger', $logger); + } + + public function testGetFor_AttachesAlwaysPresentHandlers() + { + $expectedHandler = new Monolog\Handler\NullHandler(); + $config = new LoggerConfig(array( + LoggerConfig::KEY_AlwaysLoaded => function() use ($expectedHandler) { + return array($expectedHandler); + } + )); + + /** @var \Monolog\Logger $logger */ + $logger = $config->getFor("something"); + $actualHandler = $logger->popHandler(); + + $this->assertEquals($expectedHandler, $actualHandler); + } + + public function testGetFor_AttachesSpecificHandlers() + { + $expectedHandler = new Monolog\Handler\NullHandler(); + $config = new LoggerConfig(array( + "Specific" => function() use ($expectedHandler) { + return array($expectedHandler); + } + )); + + /** @var \Monolog\Logger $logger */ + $logger = $config->getFor("Specific"); + $actualHandler = $logger->popHandler(); + + $this->assertSame($expectedHandler, $actualHandler); + } + + public function testGetFor_IgnoresAlternativeHandlers() + { + $expectedHandler = new Monolog\Handler\NullHandler(); + $alternativeHandler = new Monolog\Handler\NullHandler(); + + $config = new LoggerConfig(array( + "Specific" => function() use ($expectedHandler) { + return array($expectedHandler); + }, + "Other" => function() use ($alternativeHandler) { + return array($alternativeHandler); + } + )); + + /** @var \Monolog\Logger $logger */ + $logger = $config->getFor("Specific"); + $actualHandler = $logger->popHandler(); + + $this->assertSame($expectedHandler, $actualHandler); + $this->assertNotSame($alternativeHandler, $actualHandler); + } +} + \ No newline at end of file diff --git a/Tests/PHPCI/Plugin/EmailTest.php b/Tests/PHPCI/Plugin/EmailTest.php index 1343068c..84618001 100644 --- a/Tests/PHPCI/Plugin/EmailTest.php +++ b/Tests/PHPCI/Plugin/EmailTest.php @@ -1,32 +1,33 @@ mockBuild = $this->getMock( - '\PHPCI\Model\Build', - array('getLog', 'getStatus'), - array(), - "mockBuild", - false - ); + '\PHPCI\Model\Build', + array('getLog', 'getStatus'), + array(), + "mockBuild", + false + ); $this->mockBuild->expects($this->any()) ->method('getLog') @@ -56,27 +57,33 @@ class EmailTest extends \PHPUnit_Framework_TestCase ->method('getStatus') ->will($this->returnValue(\PHPCI\Model\Build::STATUS_SUCCESS)); - $this->mockCiBuilder = $this->getMock( - '\PHPCI\Builder', - array('getSystemConfig', - 'getBuildProjectTitle', - 'getBuild', - 'log'), - array(), - "mockBuilder", - false - ); + $this->mockCiBuilder = $this->getMock( + '\PHPCI\Builder', + array( + 'getSystemConfig', + 'getBuildProjectTitle', + 'getBuild', + 'log' + ), + array(), + "mockBuilder_email", + false + ); $this->mockCiBuilder->buildPath = "/"; $this->mockCiBuilder->expects($this->any()) ->method('getSystemConfig') ->with('phpci') - ->will($this->returnValue(array( - 'email_settings' => array( - 'from_address' => "test-from-address@example.com" + ->will( + $this->returnValue( + array( + 'email_settings' => array( + 'from_address' => "test-from-address@example.com" + ) + ) ) - ))); + ); $this->mockCiBuilder->expects($this->any()) ->method('getBuildProjectTitle') ->will($this->returnValue('Test-Project')); @@ -85,53 +92,55 @@ class EmailTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($this->mockBuild)); $this->mockMailer = $this->getMock( - '\Swift_Mailer', - array('send'), - array(), - "mockMailer", - false - ); + '\Swift_Mailer', + array('send'), + array(), + "mockMailer", + false + ); - $this->loadEmailPluginWithOptions(); - } + $this->loadEmailPluginWithOptions(); + } - protected function loadEmailPluginWithOptions($arrOptions = array()) - { - $this->testedEmailPlugin = new EmailPlugin( + protected function loadEmailPluginWithOptions($arrOptions = array()) + { + $this->testedEmailPlugin = new EmailPlugin( $this->mockCiBuilder, $this->mockBuild, $this->mockMailer, $arrOptions ); - } + } - /** - * @covers PHPUnit::execute - */ - public function testExecute_ReturnsFalseWithoutArgs() - { - $returnValue = $this->testedEmailPlugin->execute(); + /** + * @covers PHPUnit::execute + */ + public function testExecute_ReturnsFalseWithoutArgs() + { + $returnValue = $this->testedEmailPlugin->execute(); // As no addresses will have been mailed as non are configured. - $expectedReturn = false; + $expectedReturn = false; - $this->assertEquals($expectedReturn, $returnValue); - } + $this->assertEquals($expectedReturn, $returnValue); + } - /** - * @covers PHPUnit::execute - */ - public function testExecute_BuildsBasicEmails() - { - $this->loadEmailPluginWithOptions(array( - 'addresses' => array('test-receiver@example.com') - )); + /** + * @covers PHPUnit::execute + */ + public function testExecute_BuildsBasicEmails() + { + $this->loadEmailPluginWithOptions( + array( + 'addresses' => array('test-receiver@example.com') + ) + ); /** @var \Swift_Message $actualMail */ $actualMail = null; $this->catchMailPassedToSend($actualMail); - $returnValue = $this->testedEmailPlugin->execute(); - $expectedReturn = true; + $returnValue = $this->testedEmailPlugin->execute(); + $expectedReturn = true; $this->assertSystemMail( 'test-receiver@example.com', @@ -141,14 +150,14 @@ class EmailTest extends \PHPUnit_Framework_TestCase $actualMail ); - $this->assertEquals($expectedReturn, $returnValue); + $this->assertEquals($expectedReturn, $returnValue); - } + } /** - * @covers PHPUnit::sendEmail - */ + * @covers PHPUnit::sendEmail + */ public function testSendEmail_CallsMailerSend() { $this->mockMailer->expects($this->once()) @@ -157,8 +166,8 @@ class EmailTest extends \PHPUnit_Framework_TestCase } /** - * @covers PHPUnit::sendEmail - */ + * @covers PHPUnit::sendEmail + */ public function testSendEmail_BuildsAMessageObject() { $subject = "Test mail"; @@ -172,8 +181,8 @@ class EmailTest extends \PHPUnit_Framework_TestCase } /** - * @covers PHPUnit::sendEmail - */ + * @covers PHPUnit::sendEmail + */ public function testSendEmail_BuildsExpectedMessage() { $subject = "Test mail"; @@ -200,7 +209,7 @@ class EmailTest extends \PHPUnit_Framework_TestCase } /** - * @param \Swift_Message $actualMail passed by ref and populated with + * @param \Swift_Message $actualMail passed by ref and populated with * the message object the mock mailer * receives. */ @@ -227,14 +236,17 @@ class EmailTest extends \PHPUnit_Framework_TestCase * @param string $expectedSubject * @param \Swift_Message $actualMail */ - protected function assertSystemMail($expectedToAddress, - $expectedFromAddress, - $expectedBody, - $expectedSubject, - $actualMail) - { - if (! ($actualMail instanceof \Swift_Message)) { - $type = is_object($actualMail) ? get_class($actualMail) : gettype($actualMail); + protected function assertSystemMail( + $expectedToAddress, + $expectedFromAddress, + $expectedBody, + $expectedSubject, + $actualMail + ) { + if (!($actualMail instanceof \Swift_Message)) { + $type = is_object($actualMail) ? get_class($actualMail) : gettype( + $actualMail + ); throw new \Exception("Expected Swift_Message got " . $type); } $this->assertEquals( diff --git a/Tests/PHPCI/Plugin/PHPUnitTest.php b/Tests/PHPCI/Plugin/PHPUnitTest.php index 5809ac67..89b24694 100644 --- a/Tests/PHPCI/Plugin/PHPUnitTest.php +++ b/Tests/PHPCI/Plugin/PHPUnitTest.php @@ -1,71 +1,73 @@ mockCiBuilder = $this->getMock( - '\PHPCI\Builder', - array(), - array(), - "mockBuilder", - false - ); - $this->mockCiBuilder->buildPath = "/"; + /** + * @var \PHPUnit_Framework_MockObject_MockObject $mockCiBuilder + */ + protected $mockBuild; + + public function setUp() + { + $this->mockCiBuilder = $this->getMock( + '\PHPCI\Builder', + array('findBinary', 'executeCommand'), + array(), + "mockBuilder_phpUnit", + false + ); + $this->mockCiBuilder->buildPath = "/"; $this->mockBuild = $this->getMock( - '\PHPCI\Model\Build', - array(), - array(), - "MockBuild", - false - ); + '\PHPCI\Model\Build', + array(), + array(), + "MockBuild", + false + ); - $this->loadPhpUnitWithOptions(); - } + $this->loadPhpUnitWithOptions(); + } - protected function loadPhpUnitWithOptions($arrOptions = array()) - { - $this->testedPhpUnit = new PHPUnit($this->mockCiBuilder,$this->mockBuild, $arrOptions); - } + protected function loadPhpUnitWithOptions($arrOptions = array()) + { + $this->testedPhpUnit = new PhpUnit($this->mockCiBuilder, $this->mockBuild, $arrOptions); + } /** * @param \PHPUnit_Framework_MockObject_Matcher_Invocation $expectation */ - protected function expectFindBinaryToBeCalled($expectation) { + protected function expectFindBinaryToBeCalled($expectation) + { $this->mockCiBuilder->expects($expectation) - ->method("findBinary") - ->will($this->returnValue("phpunit")); + ->method("findBinary") + ->will($this->returnValue("phpunit")); } /** @@ -74,89 +76,97 @@ class PHPUnitTest extends \PHPUnit_Framework_TestCase public function expectExectuteCommandToBeCalled($expectation) { $this->mockCiBuilder->expects($expectation) - ->method("executeCommand"); + ->method("executeCommand"); } - /** - * @covers PHPUnit::execute - */ - public function testExecute_ReturnsTrueWithoutArgs() - { - $returnValue = $this->testedPhpUnit->execute(); - $expectedReturn = true; + /** + * @covers PHPUnit::execute + */ + public function testExecute_ReturnsTrueWithoutArgs() + { + $returnValue = $this->testedPhpUnit->execute(); + $expectedReturn = true; - $this->assertEquals($expectedReturn, $returnValue); - } + $this->assertEquals($expectedReturn, $returnValue); + } - /** - * @covers PHPUnit::execute - * @covers PHPUnit::runDir - */ - public function testExecute_CallsExecuteCommandOnceWhenGivenStringDirectory() - { - chdir('/'); + /** + * @covers PHPUnit::execute + * @covers PHPUnit::runDir + */ + public function testExecute_CallsExecuteCommandOnceWhenGivenStringDirectory() + { + chdir('/'); - $this->loadPhpUnitWithOptions(array( - 'directory' => "Fake/Test/Path" - )); - - $this->expectFindBinaryToBeCalled($this->once()); - $this->expectExectuteCommandToBeCalled($this->once()); - - $returnValue = $this->testedPhpUnit->execute(); - } - - /** - * @covers PHPUnit::execute - * @covers PHPUnit::runConfigFile - */ - public function testExecute_CallsExecuteCommandOnceWhenGivenStringConfig() - { - chdir('/'); - - $this->loadPhpUnitWithOptions(array( - 'config' => "Fake/Test/config.xml" - )); + $this->loadPhpUnitWithOptions( + array( + 'directory' => "Fake/Test/Path" + ) + ); $this->expectFindBinaryToBeCalled($this->once()); $this->expectExectuteCommandToBeCalled($this->once()); $returnValue = $this->testedPhpUnit->execute(); - } + } - /** - * @covers PHPUnit::execute - * @covers PHPUnit::runDir - */ - public function testExecute_CallsExecuteCommandManyTimesWhenGivenArrayDirectory() - { - chdir('/'); + /** + * @covers PHPUnit::execute + * @covers PHPUnit::runConfigFile + */ + public function testExecute_CallsExecuteCommandOnceWhenGivenStringConfig() + { + chdir('/'); - $this->loadPhpUnitWithOptions(array( - 'directory' => array(0, 1) - )); + $this->loadPhpUnitWithOptions( + array( + 'config' => "Fake/Test/config.xml" + ) + ); - $this->mockCiBuilder->expects($this->at(0))->method("executeCommand"); - $this->mockCiBuilder->expects($this->at(1))->method("executeCommand"); + $this->expectFindBinaryToBeCalled($this->once()); + $this->expectExectuteCommandToBeCalled($this->once()); - $returnValue = $this->testedPhpUnit->execute(); - } + $returnValue = $this->testedPhpUnit->execute(); + } - /** - * @covers PHPUnit::execute - * @covers PHPUnit::runConfigFile - */ - public function testExecute_CallsExecuteCommandManyTimesWhenGivenArrayConfig() - { - chdir('/'); + /** + * @covers PHPUnit::execute + * @covers PHPUnit::runDir + */ + public function testExecute_CallsExecuteCommandManyTimesWhenGivenArrayDirectory() + { + chdir('/'); - $this->loadPhpUnitWithOptions(array( - 'config' => array(0, 1) - )); + $this->loadPhpUnitWithOptions( + array( + 'directory' => array("dir1", "dir2") + ) + ); - $this->mockCiBuilder->expects($this->at(0))->method("executeCommand"); - $this->mockCiBuilder->expects($this->at(1))->method("executeCommand"); + $this->expectFindBinaryToBeCalled($this->exactly(2)); + $this->expectExectuteCommandToBeCalled($this->exactly(2)); - $returnValue = $this->testedPhpUnit->execute(); - } + $returnValue = $this->testedPhpUnit->execute(); + } + + /** + * @covers PHPUnit::execute + * @covers PHPUnit::runConfigFile + */ + public function testExecute_CallsExecuteCommandManyTimesWhenGivenArrayConfig() + { + chdir('/'); + + $this->loadPhpUnitWithOptions( + array( + 'config' => array("configfile1.xml", "configfile2.xml") + ) + ); + + $this->expectFindBinaryToBeCalled($this->exactly(2)); + $this->expectExectuteCommandToBeCalled($this->exactly(2)); + + $returnValue = $this->testedPhpUnit->execute(); + } } \ No newline at end of file diff --git a/Tests/PHPCI/Plugin/Util/ExamplePlugins.php b/Tests/PHPCI/Plugin/Util/ExamplePlugins.php index 9b5067a7..bc67bd3f 100644 --- a/Tests/PHPCI/Plugin/Util/ExamplePlugins.php +++ b/Tests/PHPCI/Plugin/Util/ExamplePlugins.php @@ -5,18 +5,28 @@ use PHPCI\Builder; use PHPCI\Model\Build; use PHPCI\Plugin; -class ExamplePluginWithNoConstructorArgs { - +class ExamplePluginWithNoConstructorArgs implements Plugin +{ + public function execute() + { + } } -class ExamplePluginWithSingleOptionalArg { +class ExamplePluginWithSingleOptionalArg implements Plugin +{ function __construct($optional = null) { } + + public function execute() + { + + } } -class ExamplePluginWithSingleRequiredArg { +class ExamplePluginWithSingleRequiredArg implements Plugin +{ public $RequiredArgument; @@ -24,9 +34,15 @@ class ExamplePluginWithSingleRequiredArg { { $this->RequiredArgument = $requiredArgument; } + + public function execute() + { + + } } -class ExamplePluginWithSingleTypedRequiredArg { +class ExamplePluginWithSingleTypedRequiredArg implements Plugin +{ public $RequiredArgument; @@ -34,6 +50,11 @@ class ExamplePluginWithSingleTypedRequiredArg { { $this->RequiredArgument = $requiredArgument; } + + public function execute() + { + + } } class ExamplePluginFull implements Plugin { diff --git a/Tests/PHPCI/Plugin/Util/ExecutorTest.php b/Tests/PHPCI/Plugin/Util/ExecutorTest.php new file mode 100644 index 00000000..ec640e95 --- /dev/null +++ b/Tests/PHPCI/Plugin/Util/ExecutorTest.php @@ -0,0 +1,146 @@ +mockBuildLogger = $this->prophesize('\PHPCI\BuildLogger'); + $this->mockFactory = $this->prophesize('\PHPCI\Plugin\Util\Factory'); + $this->testedExecutor = new Executor($this->mockFactory->reveal(), $this->mockBuildLogger->reveal()); + } + + public function testExecutePlugin_AssumesPHPCINamespaceIfNoneGiven() + { + $options = array(); + $pluginName = 'PhpUnit'; + $pluginNamespace = 'PHPCI\\Plugin\\'; + + $this->mockFactory->buildPlugin($pluginNamespace . $pluginName, $options) + ->shouldBeCalledTimes(1) + ->willReturn($this->prophesize('PHPCI\Plugin')->reveal()); + + $this->testedExecutor->executePlugin($pluginName, $options); + } + + public function testExecutePlugin_KeepsCalledNameSpace() + { + $options = array(); + $pluginName = 'ExamplePluginFull'; + $pluginNamespace = '\\PHPCI\\Plugin\\Tests\\Util\\'; + + $this->mockFactory->buildPlugin($pluginNamespace . $pluginName, $options) + ->shouldBeCalledTimes(1) + ->willReturn($this->prophesize('PHPCI\Plugin')->reveal()); + + $this->testedExecutor->executePlugin($pluginNamespace . $pluginName, $options); + } + + public function testExecutePlugin_CallsExecuteOnFactoryBuildPlugin() + { + $options = array(); + $pluginName = 'PhpUnit'; + + $mockPlugin = $this->prophesize('PHPCI\Plugin'); + $mockPlugin->execute()->shouldBeCalledTimes(1); + + $this->mockFactory->buildPlugin(Argument::any(), Argument::any())->willReturn($mockPlugin->reveal()); + + $this->testedExecutor->executePlugin($pluginName, $options); + } + + public function testExecutePlugin_ReturnsPluginSuccess() + { + $options = array(); + $pluginName = 'PhpUnit'; + + $expectedReturnValue = true; + + $mockPlugin = $this->prophesize('PHPCI\Plugin'); + $mockPlugin->execute()->willReturn($expectedReturnValue); + + $this->mockFactory->buildPlugin(Argument::any(), Argument::any())->willReturn($mockPlugin->reveal()); + + $returnValue = $this->testedExecutor->executePlugin($pluginName, $options); + + $this->assertEquals($expectedReturnValue, $returnValue); + } + + public function testExecutePlugin_LogsFailureForNonExistentClasses() + { + $options = array(); + $pluginName = 'DOESNTEXIST'; + + $this->mockBuildLogger->logFailure('Plugin does not exist: ' . $pluginName)->shouldBeCalledTimes(1); + + $this->testedExecutor->executePlugin($pluginName, $options); + } + + public function testExecutePlugin_LogsFailureWhenExceptionsAreThrownByPlugin() + { + $options = array(); + $pluginName = 'PhpUnit'; + + $expectedException = new \RuntimeException("Generic Error"); + + $mockPlugin = $this->prophesize('PHPCI\Plugin'); + $mockPlugin->execute()->willThrow($expectedException); + + $this->mockFactory->buildPlugin(Argument::any(), Argument::any())->willReturn($mockPlugin->reveal()); + + $this->mockBuildLogger->logFailure('EXCEPTION: ' . $expectedException->getMessage(), $expectedException) + ->shouldBeCalledTimes(1); + + $this->testedExecutor->executePlugin($pluginName, $options); + } + + public function testExecutePlugins_CallsEachPluginForStage() + { + $phpUnitPluginOptions = array(); + $behatPluginOptions = array(); + + $config = array( + 'stageOne' => array( + 'PhpUnit' => $phpUnitPluginOptions, + 'Behat' => $behatPluginOptions, + ) + ); + + $pluginNamespace = 'PHPCI\\Plugin\\'; + + $mockPhpUnitPlugin = $this->prophesize('PHPCI\Plugin'); + $mockPhpUnitPlugin->execute()->shouldBeCalledTimes(1)->willReturn(true); + + $this->mockFactory->buildPlugin($pluginNamespace . 'PhpUnit', $phpUnitPluginOptions) + ->willReturn($mockPhpUnitPlugin->reveal()); + + + $mockBehatPlugin = $this->prophesize('PHPCI\Plugin'); + $mockBehatPlugin->execute()->shouldBeCalledTimes(1)->willReturn(true); + + $this->mockFactory->buildPlugin($pluginNamespace . 'Behat', $behatPluginOptions) + ->willReturn($mockBehatPlugin->reveal()); + + + $this->testedExecutor->executePlugins($config, 'stageOne'); + } + +} + \ No newline at end of file diff --git a/Tests/PHPCI/Plugin/Util/FactoryTest.php b/Tests/PHPCI/Plugin/Util/FactoryTest.php index fb2794c8..3265e07c 100644 --- a/Tests/PHPCI/Plugin/Util/FactoryTest.php +++ b/Tests/PHPCI/Plugin/Util/FactoryTest.php @@ -37,10 +37,16 @@ class FactoryTest extends \PHPUnit_Framework_TestCase { public function testRegisterResourceThrowsExceptionWithoutTypeAndName() { - $this->setExpectedException("InvalidArgumentException"); + $this->setExpectedException('InvalidArgumentException', 'Type or Name must be specified'); $this->testedFactory->registerResource($this->resourceLoader, null, null); } + public function testRegisterResourceThrowsExceptionIfLoaderIsntFunction() + { + $this->setExpectedException('InvalidArgumentException', '$loader is expected to be a function'); + $this->testedFactory->registerResource(array("dummy"), "TestName", "TestClass"); + } + public function testBuildPluginWorksWithConstructorlessPlugins() { $namespace = '\\PHPCI\\Plugin\\Tests\\Util\\'; @@ -49,6 +55,12 @@ class FactoryTest extends \PHPUnit_Framework_TestCase { $this->assertInstanceOf($expectedPluginClass, $plugin); } + public function testBuildPluginFailsForNonPluginClasses() + { + $this->setExpectedException('InvalidArgumentException', 'Requested class must implement \PHPCI\Plugin'); + $plugin = $this->testedFactory->buildPlugin("stdClass"); + } + public function testBuildPluginWorksWithSingleOptionalArgConstructor() { $namespace = '\\PHPCI\\Plugin\\Tests\\Util\\'; diff --git a/bootstrap.php b/bootstrap.php index 17aaf608..67b9fbaf 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -47,7 +47,9 @@ $conf['b8']['app']['namespace'] = 'PHPCI'; $conf['b8']['app']['default_controller'] = 'Home'; $conf['b8']['view']['path'] = dirname(__FILE__) . '/PHPCI/View/'; -$config = new b8\Config($conf); -$config->loadYaml(dirname(__FILE__) . '/PHPCI/config.yml'); +if (file_exists(dirname(__FILE__) . '/PHPCI/config.yml')) { + $config = new b8\Config($conf); + $config->loadYaml(dirname(__FILE__) . '/PHPCI/config.yml'); +} require_once(dirname(__FILE__) . '/vars.php'); diff --git a/composer.json b/composer.json index 59d9c930..acafd3a5 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,10 @@ "pimple/pimple": "1.1.*" }, + "require-dev": { + "phpspec/prophecy-phpunit": "1.*" + }, + "suggest": { "phpunit/phpunit": "PHP unit testing framework", "phpmd/phpmd": "PHP Mess Detector", diff --git a/composer.lock b/composer.lock index 278e483a..ce6ac387 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "2f0615871ce4ee1eb8e4642bf0c731da", + "hash": "07f37de4c8bacd8a1a7b6e14269178d1", "packages": [ { "name": "block8/b8framework", @@ -239,16 +239,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.0.2", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379" + "reference": "32edc3b0de0fdc1b10f5c4912e8677b3f411a230" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/f3917ecef35a4e4d98b303eb9fee463bc983f379", - "reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/32edc3b0de0fdc1b10f5c4912e8677b3f411a230", + "reference": "32edc3b0de0fdc1b10f5c4912e8677b3f411a230", "shasum": "" }, "require": { @@ -284,21 +284,21 @@ "mail", "mailer" ], - "time": "2013-08-30 12:35:21" + "time": "2013-12-03 13:33:24" }, { "name": "symfony/console", - "version": "v2.3.7", + "version": "v2.4.0", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "00848d3e13cf512e77c7498c2b3b0192f61f4b18" + "reference": "3c1496ae96d24ccc6c340fcc25f71d7a1ab4c12c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/00848d3e13cf512e77c7498c2b3b0192f61f4b18", - "reference": "00848d3e13cf512e77c7498c2b3b0192f61f4b18", + "url": "https://api.github.com/repos/symfony/Console/zipball/3c1496ae96d24ccc6c340fcc25f71d7a1ab4c12c", + "reference": "3c1496ae96d24ccc6c340fcc25f71d7a1ab4c12c", "shasum": "" }, "require": { @@ -313,7 +313,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -337,21 +337,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2013-11-13 21:27:40" + "time": "2013-11-27 09:10:40" }, { "name": "symfony/yaml", - "version": "v2.3.7", + "version": "v2.4.0", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "c1bda5b459d792cb253de12c65beba3040163b2b" + "reference": "1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/c1bda5b459d792cb253de12c65beba3040163b2b", - "reference": "c1bda5b459d792cb253de12c65beba3040163b2b", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5", + "reference": "1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5", "shasum": "" }, "require": { @@ -360,7 +360,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -384,11 +384,115 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2013-10-17 11:48:01" + "time": "2013-11-26 16:40:27" } ], "packages-dev": [ - + { + "name": "phpspec/prophecy", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "79d9c8bd94801bffbf9b56964f6438762da6d8cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/79d9c8bd94801bffbf9b56964f6438762da6d8cd", + "reference": "79d9c8bd94801bffbf9b56964f6438762da6d8cd", + "shasum": "" + }, + "require-dev": { + "phpspec/phpspec": "2.0.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "http://phpspec.org", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2013-08-10 11:11:45" + }, + { + "name": "phpspec/prophecy-phpunit", + "version": "v1.0.0", + "target-dir": "Prophecy/PhpUnit", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy-phpunit.git", + "reference": "ebc983be95b026fcea18afb7870e7b9041dc9d11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/ebc983be95b026fcea18afb7870e7b9041dc9d11", + "reference": "ebc983be95b026fcea18afb7870e7b9041dc9d11", + "shasum": "" + }, + "require": { + "phpspec/prophecy": "~1.0" + }, + "suggest": { + "phpunit/phpunit": "if it is not installed globally" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\PhpUnit\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "PhpUnit test case integrating the Prophecy mocking library", + "homepage": "http://phpspec.org", + "keywords": [ + "phpunit", + "prophecy" + ], + "time": "2013-07-04 21:27:53" + } ], "aliases": [ diff --git a/console b/console index 3358dce7..f855f5ab 100644 --- a/console +++ b/console @@ -20,15 +20,15 @@ use PHPCI\Command\DaemonCommand; use PHPCI\Command\PollCommand; use Symfony\Component\Console\Application; -$loggerConfig = new \PHPCI\Helper\LoggerConfig(__DIR__ . "/loggerconfig.php"); +$loggerConfig = \PHPCI\Helper\LoggerConfig::newFromFile(__DIR__ . "/loggerconfig.php"); $application = new Application(); -$application->add(new RunCommand($loggerConfig->GetFor('RunCommand'))); +$application->add(new RunCommand($loggerConfig->getFor('RunCommand'))); $application->add(new InstallCommand); -$application->add(new UpdateCommand($loggerConfig->GetFor('UpdateCommand'))); +$application->add(new UpdateCommand($loggerConfig->getFor('UpdateCommand'))); $application->add(new GenerateCommand); -$application->add(new DaemonCommand($loggerConfig->GetFor('DaemonCommand'))); -$application->add(new PollCommand($loggerConfig->GetFor('PollCommand'))); +$application->add(new DaemonCommand($loggerConfig->getFor('DaemonCommand'))); +$application->add(new PollCommand($loggerConfig->getFor('PollCommand'))); $application->run(); diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index d662825b..7cf07458 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -1,18 +1,235 @@ +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind + * for the details of code below + */ +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () { + }, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} + /** * Used for delete buttons in the system, just to prevent accidental clicks. */ -function confirmDelete(url) -{ - if(confirm('Are you sure you want to delete this?')) - { - window.location.href = url; - } - else - { - return false; - } +function confirmDelete(url, subject, reloadAfter) { + + var dialog = new PHPCIConfirmDialog({ + message: subject + ' will be permanently deleted. Are you sure?', + confirmBtnCaption: 'Delete', + /* + confirm-btn click handler + */ + confirmed: function (e) { + var dialog = this; + e.preventDefault(); + + /* + Call delete URL + */ + $.ajax({ + url: url, + 'success': function (data) { + if (reloadAfter) { + dialog.onClose = function () { + window.location.reload(); + }; + } + + dialog.showStatusMessage('Successfully deleted!', 1000); + }, + 'error': function (data) { + dialog.showStatusMessage('Deletion failed! Server says "' + data.statusText + '"'); + } + }); + } + }); + + dialog.show(); + return dialog; } +/** + * PHPCIConfirmDialog constructor options object + * @type {{message: string, title: string, confirmBtnCaption: string, cancelBtnCaption: string, confirmed: Function}} + */ +var PHPCIConfirmDialogOptions = { + message: 'The action will be performed and cannot be undone. Are you sure?', + title: 'Confirmation Dialog', + confirmBtnCaption: 'Ok', + cancelBtnCaption: 'Cancel', + confirmed: function (e) { + this.close(); + } +}; + +var PHPCIConfirmDialog = Class.extend({ + /** + * @private + * @var {bool} Determines whether the dialog has been confirmed + */ + confirmed: false, + + /** + * @param {PHPCIConfirmDialogOptions} options + */ + init: function (options) { + + options = options ? $.extend(PHPCIConfirmDialogOptions, options) : PHPCIConfirmDialogOptions; + + if (!$('#confirm-dialog').length) { + /* + Add the dialog html to a page on first use. No need to have it there before first use. + */ + $('body').append( + '' + ); + } + + /* + Define dialog controls + */ + this.$dialog = $('#confirm-dialog'); + this.$cancelBtn = this.$dialog.find('div.modal-footer button.btn-default'); + this.$confirmBtn = this.$dialog.find('div.modal-footer button.btn-primary'); + this.$title = this.$dialog.find('h4.modal-title'); + this.$body = this.$dialog.find('div.modal-body'); + + /* + Initialize its values + */ + this.$title.html(options.title ? options.title : PHPCIConfirmDialogOptions.title); + this.$body.html(options.message ? options.message : PHPCIConfirmDialogOptions.message); + this.$confirmBtn.html( + options.confirmBtnCaption ? options.confirmBtnCaption : PHPCIConfirmDialogOptions.confirmBtnCaption + ); + + this.$cancelBtn.html( + options.cancelBtnCaption ? options.cancelBtnCaption : PHPCIConfirmDialogOptions.cancelBtnCaption + ); + + /* + Events + */ + this.confirmBtnClick = options.confirmed; + + /* + Re-bind handlers + */ + this.$confirmBtn.unbind('click'); + this.$confirmBtn.click(this.onConfirm.bind(this)); + + this.$confirmBtn.unbind('hidden.bs.modal'); + + /* + Bind the close event of the dialog to the set of onClose* methods + */ + this.$dialog.on('hidden.bs.modal', function () {this.onClose()}.bind(this)); + this.$dialog.on('hidden.bs.modal', function () { + if (this.confirmed) { + this.onCloseConfirmed(); + } else { + this.onCloseCanceled(); + } + }.bind(this)); + + /* + Restore state if was changed previously + */ + this.$cancelBtn.show(); + this.$confirmBtn.show(); + this.confirmed = false; + }, + + /** + * Show dialog + */ + show: function () { + this.$dialog.modal('show'); + }, + + /** + * Hide dialog + */ + close: function () { + this.$dialog.modal('hide'); + }, + + onConfirm: function (e) { + this.confirmed = true; + $(this).attr('disabled', 'disabled'); + this.confirmBtnClick(e); + }, + + /** + * Called only when confirmed dialog was closed + */ + onCloseConfirmed: function () {}, + + /** + * Called only when canceled dialog was closed + */ + onCloseCanceled: function () {}, + + /** + * Called always when the dialog was closed + */ + onClose: function () {}, + + showStatusMessage: function (message, closeTimeout) { + this.$confirmBtn.hide(); + this.$cancelBtn.html('Close'); + + /* + Status message + */ + this.$body.html(message); + + if (closeTimeout) { + window.setTimeout(function () { + /* + Hide the dialog + */ + this.close(); + }.bind(this), closeTimeout); + } + } +}); + /** * Used to initialise the project form: */ diff --git a/public/install.php b/public/install.php index d7ef1973..1d900800 100644 --- a/public/install.php +++ b/public/install.php @@ -5,7 +5,7 @@ require_once(dirname(__FILE__) . '/../bootstrap.php'); $installStage = 'start'; $formAction = ''; $config = array(); -$ciUrl = ($_SERVER['HTTPS'] == "on" ? 'https' : 'http') . '://'; +$ciUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? 'https' : 'http') . '://'; $ciUrl .= $_SERVER['HTTP_HOST']; $ciUrl .= str_replace('/install.php', '', $_SERVER['REQUEST_URI']); diff --git a/vars.php b/vars.php index 89a1d81e..5725805e 100644 --- a/vars.php +++ b/vars.php @@ -7,7 +7,7 @@ if (!defined('APPLICATION_PATH')) { } // Define our PHPCI_URL, if not already defined: -if (!defined('PHPCI_URL')) { +if (!defined('PHPCI_URL') && isset($config)) { define('PHPCI_URL', $config->get('phpci.url', '') . '/'); }