diff --git a/PHPCI/Command/PollCommand.php b/PHPCI/Command/PollCommand.php new file mode 100644 index 00000000..af91acec --- /dev/null +++ b/PHPCI/Command/PollCommand.php @@ -0,0 +1,101 @@ + + * @package PHPCI + * @subpackage Console + */ +class PollCommand extends Command +{ + /** + * @var \Monolog\Logger + */ + protected $logger; + + public function __construct(Logger $logger, $name = null) + { + parent::__construct($name); + $this->logger = $logger; + } + + protected function configure() + { + $this + ->setName('phpci:poll-github') + ->setDescription('Poll github to check if we need to start a build.'); + } + + /** + * Pulls all pending builds from the database and runs them. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $parser = new Parser(); + $yaml = file_get_contents(APPLICATION_PATH . 'PHPCI/config.yml'); + $this->settings = $parser->parse($yaml); + + $token = $this->settings['phpci']['github']['token']; + + if (!$token) { + $this->logger->error("No github token found"); + exit(); + } + + $buildStore = Factory::getStore('Build'); + + $this->logger->addInfo("Finding projects to poll"); + $projectStore = Factory::getStore('Project'); + $result = $projectStore->getWhere(); + $this->logger->addInfo(sprintf("Found %d projects", count($result['items']))); + + foreach ($result['items'] as $project) { + $http = new HttpClient('https://api.github.com'); + $commits = $http->get('/repos/' . $project->getReference() . '/commits', array('access_token' => $token)); + + $last_commit = $commits['body'][0]['sha']; + + $this->logger->info("Last commit to github for " . $project->getTitle() . " is " . $last_commit); + + if ($project->getLastCommit() != $last_commit && $last_commit != "") { + $this->logger->info("Last commit is different from database, adding new build for " . $project->getTitle()); + + $build = new Build(); + $build->setProjectId($project->getId()); + $build->setCommitId($last_commit); + $build->setStatus(0); + $build->setBranch($project->getType() === 'hg' ? 'default' : 'master'); + $build->setCreated(new \DateTime()); + + $buildStore->save($build); + + $project->setLastCommit($last_commit); + $projectStore->save($project); + } + } + + $this->logger->addInfo("Finished processing builds"); + } +} + diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index d0582f6a..9fce197d 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -53,9 +53,16 @@ class SettingsController extends Controller $this->settings['phpci']['github']['id'] = $this->getParam('githubid', ''); $this->settings['phpci']['github']['secret'] = $this->getParam('githubsecret', ''); - $this->storeSettings(); + $error = $this->storeSettings(); - header('Location: ' . PHPCI_URL . 'settings?saved=1'); + if($error) + { + header('Location: ' . PHPCI_URL . 'settings?saved=2'); + } + else + { + header('Location: ' . PHPCI_URL . 'settings?saved=1'); + } die; } @@ -94,6 +101,7 @@ 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']; } protected function getGithubForm() diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index f23e1d7f..e3e12ecc 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -40,6 +40,7 @@ class ProjectBase extends Model 'type' => null, 'token' => null, 'access_information' => null, + 'last_commit' => null, ); /** @@ -54,6 +55,7 @@ class ProjectBase extends Model 'type' => 'getType', 'token' => 'getToken', 'access_information' => 'getAccessInformation', + 'last_commit' => 'getLastCommit', // Foreign key getters: ); @@ -70,6 +72,7 @@ class ProjectBase extends Model 'type' => 'setType', 'token' => 'setToken', 'access_information' => 'setAccessInformation', + 'last_commit' => 'setLastCommit', // Foreign key setters: ); @@ -115,6 +118,12 @@ class ProjectBase extends Model 'nullable' => true, 'default' => null, ), + 'last_commit' => array( + 'type' => 'varchar', + 'length' => 250, + 'nullable' => true, + 'default' => null, + ), ); /** @@ -215,6 +224,18 @@ class ProjectBase extends Model return $rtn; } + /** + * Get the value of LastCommit / last_commit. + * + * @return string + */ + public function getLastCommit() + { + $rtn = $this->data['last_commit']; + + return $rtn; + } + /** * Set the value of Id / id. * @@ -349,6 +370,24 @@ class ProjectBase extends Model $this->_setModified('access_information'); } + /** + * Set the value of LastCommit / last_commit. + * + * @param $value string + */ + public function setLastCommit($value) + { + $this->_validateString('LastCommit', $value); + + if ($this->data['last_commit'] === $value) { + return; + } + + $this->data['last_commit'] = $value; + + $this->_setModified('last_commit'); + } + /** * Get Build models by ProjectId for this Project. * diff --git a/PHPCI/Model/Build/LocalBuild.php b/PHPCI/Model/Build/LocalBuild.php index bfb8ac9e..08d997dd 100644 --- a/PHPCI/Model/Build/LocalBuild.php +++ b/PHPCI/Model/Build/LocalBuild.php @@ -33,7 +33,7 @@ class LocalBuild extends Build // If there's a /config file in the reference directory, it is probably a bare repository // which we'll extract into our build path directly. if (is_file($reference.'/config') && $this->handleBareRepository($builder, $reference, $buildPath) === true) { - return true; + return $this->handleConfig($builder, $buildPath) !== false; } $buildSettings = $this->handleConfig($builder, $reference); @@ -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('git --git-dir="%s" archive master | tar -x -C "%s"', $reference, $buildPath); + $builder->executeCommand('mkdir %2$s; git --git-dir="%1$s" archive master | tar -x -C "%2$s"', $reference, $buildPath); return true; } diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php index 44fa5893..2f0d7592 100755 --- a/PHPCI/Plugin/PhpCodeSniffer.php +++ b/PHPCI/Plugin/PhpCodeSniffer.php @@ -120,7 +120,7 @@ class PhpCodeSniffer implements \PHPCI\Plugin return false; } - $cmd = $phpcs . ' %s %s %s %s %s "%s"'; + $cmd = $phpcs . ' --report=emacs %s %s %s %s %s "%s"'; $success = $this->phpci->executeCommand( $cmd, $standard, @@ -134,12 +134,12 @@ class PhpCodeSniffer implements \PHPCI\Plugin $output = $this->phpci->getLastOutput(); $matches = array(); - if (preg_match_all('/WARNING/', $output, $matches)) { + if (preg_match_all('/\: warning \-/', $output, $matches)) { $this->build->storeMeta('phpcs-warnings', count($matches[0])); } $matches = array(); - if (preg_match_all('/ERROR/', $output, $matches)) { + if (preg_match_all('/\: error \-/', $output, $matches)) { $this->build->storeMeta('phpcs-errors', count($matches[0])); } diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php index 66944ad6..89c22f66 100755 --- a/PHPCI/Plugin/PhpMessDetector.php +++ b/PHPCI/Plugin/PhpMessDetector.php @@ -86,7 +86,7 @@ class PhpMessDetector implements \PHPCI\Plugin } foreach ($this->rules as &$rule) { - if ($rule[0] !== '/' && strpos($rule, '/') !== false) { + if (strpos($rule, '/') !== false) { $rule = $this->phpci->buildPath . $rule; } } @@ -115,7 +115,7 @@ class PhpMessDetector implements \PHPCI\Plugin protected function overrideSetting($options, $key) { - if (isset($options[$key]) && is_array($options['key'])) { + if (isset($options[$key]) && is_array($options[$key])) { $this->{$key} = $options[$key]; } } diff --git a/PHPCI/Plugin/PhpParallelLint.php b/PHPCI/Plugin/PhpParallelLint.php index b7a8cd24..282098cc 100644 --- a/PHPCI/Plugin/PhpParallelLint.php +++ b/PHPCI/Plugin/PhpParallelLint.php @@ -20,15 +20,40 @@ use PHPCI\Model\Build; */ class PhpParallelLint implements \PHPCI\Plugin { - protected $directory; - protected $preferDist; + /** + * @var \PHPCI\Builder + */ protected $phpci; + /** + * @var \PHPCI\Model\Build + */ + protected $build; + + /** + * @var string + */ + protected $directory; + + /** + * @var array - paths to ignore + */ + protected $ignore; + public function __construct(Builder $phpci, Build $build, array $options = array()) { - $path = $phpci->buildPath; $this->phpci = $phpci; - $this->directory = isset($options['directory']) ? $path . $options['directory'] : $path; + $this->build = $build; + $this->directory = $phpci->buildPath; + $this->ignore = $this->phpci->ignore; + + if (isset($options['directory'])) { + $this->directory = $options['directory']; + } + + if (isset($options['ignore'])) { + $this->ignore = $options['ignore']; + } } /** @@ -36,10 +61,39 @@ class PhpParallelLint implements \PHPCI\Plugin */ public function execute() { - // build the parallel lint command - $cmd = "run %s"; + list($ignore) = $this->getFlags(); - // and execute it - return $this->phpci->executeCommand(PHPCI_BIN_DIR . $cmd, $this->directory); + $phplint = $this->phpci->findBinary('parallel-lint'); + + if (!$phplint) { + $this->phpci->logFailure('Could not find parallel-lint.'); + return false; + } + + $cmd = $phplint . ' %s "%s"'; + $success = $this->phpci->executeCommand( + $cmd, + $ignore, + $this->directory + ); + + $output = $this->phpci->getLastOutput(); + + $matches = array(); + if (preg_match_all('/Parse error\:/', $output, $matches)) { + $this->build->storeMeta('phplint-errors', count($matches[0])); + } + + return $success; + } + + protected function getFlags() + { + $ignore = ''; + if (count($this->ignore)) { + $ignore = ' --exclude ' . implode(' --exclude ', $this->ignore); + } + + return array($ignore); } } diff --git a/PHPCI/Plugin/Util/Factory.php b/PHPCI/Plugin/Util/Factory.php index c0234374..0e526023 100644 --- a/PHPCI/Plugin/Util/Factory.php +++ b/PHPCI/Plugin/Util/Factory.php @@ -84,7 +84,7 @@ class Factory { * @throws \InvalidArgumentException * @internal param mixed $resource */ - public function registerResource(callable $loader, + public function registerResource($loader, $name = null, $type = null ) @@ -95,6 +95,12 @@ class Factory { ); } + if (!($loader instanceof \Closure)) { + throw new \InvalidArgumentException( + '$loader is expected to be a function' + ); + } + $resourceID = $this->getInternalID($type, $name); $this->container[$resourceID] = $loader; diff --git a/PHPCI/View/Settings/index.phtml b/PHPCI/View/Settings/index.phtml index c8206811..a88b214e 100644 --- a/PHPCI/View/Settings/index.phtml +++ b/PHPCI/View/Settings/index.phtml @@ -1,9 +1,15 @@ - +

Your settings have been saved.

+ +

+ Your settings could not be saved, maybe check the permissions of config.yml? +

+ +

Your Github account has been linked. diff --git a/console b/console old mode 100755 new mode 100644 index fc639be4..3358dce7 --- a/console +++ b/console @@ -17,6 +17,7 @@ use PHPCI\Command\GenerateCommand; use PHPCI\Command\UpdateCommand; use PHPCI\Command\InstallCommand; use PHPCI\Command\DaemonCommand; +use PHPCI\Command\PollCommand; use Symfony\Component\Console\Application; $loggerConfig = new \PHPCI\Helper\LoggerConfig(__DIR__ . "/loggerconfig.php"); @@ -28,5 +29,6 @@ $application->add(new InstallCommand); $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->run(); diff --git a/public/assets/js/build-plugins/warnings.js b/public/assets/js/build-plugins/warnings.js index b77d7bfb..c485dd4e 100644 --- a/public/assets/js/build-plugins/warnings.js +++ b/public/assets/js/build-plugins/warnings.js @@ -11,8 +11,9 @@ var plugin = PHPCI.UiPlugin.extend({ var query1 = PHPCI.registerQuery('phpmd-warnings', -1, {num_builds: 10, key: 'phpmd-warnings'}) var query2 = PHPCI.registerQuery('phpcs-warnings', -1, {num_builds: 10, key: 'phpcs-warnings'}) var query3 = PHPCI.registerQuery('phpcs-errors', -1, {num_builds: 10, key: 'phpcs-errors'}) + var query4 = PHPCI.registerQuery('phplint-errors', -1, {num_builds: 10, key: 'phplint-errors'}) - $(window).on('phpmd-warnings phpcs-warnings phpcs-errors', function(data) { + $(window).on('phpmd-warnings phpcs-warnings phpcs-errors phplint-errors', function(data) { self.onUpdate(data); }); @@ -22,6 +23,7 @@ var plugin = PHPCI.UiPlugin.extend({ query1(); query2(); query3(); + query4(); } }); @@ -67,7 +69,12 @@ var plugin = PHPCI.UiPlugin.extend({ var keys = self.keys; for (var i in keys) { - var t = {'phpmd-warnings': 'PHPMD Warnings', 'phpcs-warnings': 'PHPCS Warnings', 'phpcs-errors': 'PHPCS Errors'}; + var t = { + 'phpmd-warnings': 'PHPMD Warnings', + 'phpcs-warnings': 'PHPCS Warnings', + 'phpcs-errors': 'PHPCS Errors', + 'phplint-errors': 'PHPLint Errors' + }; titles.push(t[keys[i]]); } diff --git a/public/install.php b/public/install.php index 0b7df4c8..d7ef1973 100644 --- a/public/install.php +++ b/public/install.php @@ -93,7 +93,7 @@ if ($installOK && strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') { unset($config['tmp']); $dumper = new \Symfony\Component\Yaml\Dumper(); - $yaml = $dumper->dump($config); + $yaml = $dumper->dump($config, 5); file_put_contents(PHPCI_DIR . 'PHPCI/config.yml', $yaml);