diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..18a7adc3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,121 @@ +Change Log +========== + +## [PHP Censor v0.6.0](https://github.com/corpsee/php-censor/tree/0.6.0) (2017-01-22) + +[Full Changelog](https://github.com/corpsee/php-censor/compare/0.5.0...0.6.0) + +* Added pluggable authentication and LDAP authentication provider + +```yml +php-censor: + security: + auth_providers: + internal: + type: internal + ldap-php-censor: + type: ldap + data: + host: 'ldap.php-censor.local' + port: 389 + base_dn: 'dc=php-censor,dc=local' + mail_attribute: mail +``` + +If you enter by new LDAP-user, the record in the DB will be created automatically. The basement of the feature is @Adirelle and @dzolotov code. + +* **Unified application configuration (app/config.yml) authentication options** + +The old way to disable authentication: + +```yml +php-censor: + autentication_settings: + state: true + user_id: 1 +``` + +And a new way: + +```yml +php-censor: + security: + disable_auth: true + default_user_id: 1 +``` + +## [PHP Censor v0.5.0](https://github.com/corpsee/php-censor/tree/0.5.0) (2017-01-21) + +[Full Changelog](https://github.com/corpsee/php-censor/compare/0.4.0...0.5.0) + +* Fixed projects archive (Archived projects can not be built and projects moved to the archive section) +* Added option to the application configuration (`app/config.yml`) to allow/deny removing the build directory after build (`php-censor.build.remove_builds`) + +```yml +php-censor: + build: + remove_builds: true +``` + +* Added options to the application configuration (`app/config.yml`) to allow/deny sending errors in the commits/pull requests as comments on Github (`php-censor.github.comments.commit` and `php-censor.github.comments.pull_request`) + +```yml +php-censor: + github: + token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + comments: + commit: false + pull_request: false +``` + +* Improved plugin Codeception +* **Removed agent/worker Daemon mode (You should use Worker mode instead)** +* **Removed pluginconfig configuration file (You should use plugin full name including the namespace)** + +```yml +test: + \PluginNamespace\Plugin: + allow_failures: true +``` + +## [PHP Censor v0.4.0](https://github.com/corpsee/php-censor/tree/0.4.0) (2017-01-15) + +[Full Changelog](https://github.com/corpsee/php-censor/compare/0.3.0...0.4.0) + +* Fixed delete confirmation for all items +* Added ajax update for the main page (dashboard) +* Added public status information to the project page +* UI and localization fixes + +## [PHP Censor v0.3.0](https://github.com/corpsee/php-censor/tree/0.3.0) (2017-01-11) + +[Full Changelog](https://github.com/corpsee/php-censor/compare/0.2.0...0.3.0) + +* Improved UI +* Updated dependencies +* Updated PHPUnit from 4.8 to 5.7 +* Improved build without config + +## [PHP Censor v0.2.0](https://github.com/corpsee/php-censor/tree/0.2.0) (2017-01-07) + +[Full Changelog](https://github.com/corpsee/php-censor/compare/0.1.0...0.2.0) + +* Improved PHPUnit plugin +* Improved UI +* Added login by name (name or email) +* Fixed public build status page + +## [PHP Censor v0.1.0](https://github.com/corpsee/php-censor/tree/0.1.0) (2017-01-04) + +Initial release. Changes from PHPCI (1.7.1): + +* Upped PHP minimal version from 5.3 to 5.6 +* Fixed tests and other small fixes +* Redesigned project structure +* Added more debug info into the build log +* Moved CSS/JS dependencies from sources to Composer dependencies (asset-packagist.org) +* Added item per page parameter for build list + +## PHP Censor v0 (2016-06-23) + +Project started diff --git a/README.md b/README.md index 84a5bf50..d19f9c48 100644 --- a/README.md +++ b/README.md @@ -8,31 +8,46 @@ PHP Censor ========== -**PHP Censor** is a fork of [PHPCI](https://www.phptesting.org) -(And [B8Framework](https://github.com/Block8/b8framework)) and is a open source -([BSD-2-Clause license](LICENSE.md)) continuous integration tool specifically designed for PHP. - -What it does ------------- - -* Clones your project from Github, Bitbucket, Gitlab or Git; - -* Allows you to set up and tear down test databases; - -* Installs your project's Composer dependencies; - -* Runs through any combination of the [supported plugins](docs/en/README.md)); +**PHP Censor** is a open source self-hosted continuous integration server for PHP projects (Fork of +[PHPCI](https://www.phptesting.org)). [](docs/screenshots/dashboard.png) More [screenshots](docs/en/screenshots.md). +Features +-------- + +* Clone project from Github, Bitbucket, Gitlab, Git, Mercurial, SVN or from local directory; + +* Set up and tear down database tests for PostgreSQL, MySQL or SQLite; + +* Install Composer dependencies; + +* Run tests for PHPUnit, Atoum, Behat, Codeception and PHPSpec; + +* Check code via Lint, PHPParallelLint, Pdepend, PHPCodeSniffer, PHPCpd, PHPCsFixer, PHPDocblockChecker, PHPLoc, +PHPMessDetect, PHPTalLint and TechnicalDept; + +* Run through any combination of the other [supported plugins](docs/en/README.md), including Campfire, CleanBuild, +CopyBuild, Deployer, Env, Git, Grunt, Gulp, PackageBuild, Phar, Phing, Shell and Wipe; + +* Send notifications on Email, XMPP, Slack, IRC, Flowdock and HipChat; + Configuring ----------- -Similar to [TravisCI](https://travis-ci.org), to support PHP Censor in your project, you simply need to add a `.php-censor.yml` -(`phpci.yml`/`.phpci.yml` for backward compatibility with PHPCI) file to the root of your repository. The file should -look something like this: +There are several ways to set up the project: + +* Add project without any project config (Runs "zero-config" plugins, including: Composer, TechnicalDept, PHPLoc, +PHPCpd, PHPCodeSniffer, PHPMessDetect, PHPDocblockChecker, PHPParallelLint, PHPUnit and Codeception); + +* Similar to [Travis CI](https://travis-ci.org), to support PHP Censor in your project, you simply need to add a +`.php-censor.yml` (`phpci.yml`/`.phpci.yml` for backward compatibility with PHPCI) file to the root of your repository; + +* Add project config in PHP Censor project page (And it will cancel file config from project repository); + +The project config should look something like this: ```yml setup: @@ -91,20 +106,7 @@ Updating * Update the PHP Censor database: `./bin/console php-censor-migrations:migrate`; -* Update the Composer dependencies: `composer update` - -Tests ------ - -```bash -cd /path/to/php-censor -./vendor/bin/phpunit -``` - -For Phar plugin tests set 'phar.readonly' setting to Off (0) in `php.ini` config. Otherwise tests will be skipped. - -For database B8Framework tests create empty 'b8_test' MySQL database on 'localhost' with user/password: `root/root`. -Otherwise database tests will be skipped. +* Update the Composer dependencies: `composer install` Migrations ---------- @@ -123,6 +125,19 @@ cd /path/to/php-censor ./bin/console php-censor-migrations:create NewMigrationName ``` +Tests +----- + +```bash +cd /path/to/php-censor +./vendor/bin/phpunit +``` + +For Phar plugin tests set 'phar.readonly' setting to Off (0) in `php.ini` config. Otherwise tests will be skipped. + +For database B8Framework tests create empty 'b8_test' MySQL database on 'localhost' with user/password: `root/root`. +Otherwise database tests will be skipped. + Documentation ------------- diff --git a/app/config.example.yml b/app/config.example.yml index b0574a30..c7c05952 100644 --- a/app/config.example.yml +++ b/app/config.example.yml @@ -14,9 +14,10 @@ php-censor: email_settings: from_address: 'no-reply@php-censor.local' smtp_address: - worker: - host: localhost - queue: php-censor-queue + queue: + host: localhost + name: php-censor-queue + lifetime: 600 github: token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' comments: diff --git a/public/robots.txt b/public/robots.txt index 0ad279c7..c6742d8a 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,2 @@ User-Agent: * -Disallow: +Disallow: / diff --git a/src/PHPCensor/Application.php b/src/PHPCensor/Application.php index c338c227..a469be95 100644 --- a/src/PHPCensor/Application.php +++ b/src/PHPCensor/Application.php @@ -104,7 +104,7 @@ class Application extends b8\Application $this->response->setContent($view->render()); } - if ($this->response->hasLayout() && $this->controller->layout) { + if ($this->response->hasLayout() && $this->controller && $this->controller->layout) { $this->setLayoutVariables($this->controller->layout); $this->controller->layout->content = $this->response->getContent(); diff --git a/src/PHPCensor/Command/InstallCommand.php b/src/PHPCensor/Command/InstallCommand.php index e4b31f06..8bbfbbd3 100644 --- a/src/PHPCensor/Command/InstallCommand.php +++ b/src/PHPCensor/Command/InstallCommand.php @@ -244,8 +244,8 @@ class InstallCommand extends Command $config['language'] = 'en'; $config['per_page'] = 10; - $config['url'] = $url; - $config['worker'] = $this->getQueueInformation($input, $output, $helper); + $config['url'] = $url; + $config['queue'] = $this->getQueueInformation($input, $output, $helper); return $config; } @@ -278,9 +278,9 @@ class InstallCommand extends Command $rtn['host'] = $helper->ask($input, $output, $questionQueue); } - if (!$rtn['queue'] = $input->getOption('queue-name')) { + if (!$rtn['name'] = $input->getOption('queue-name')) { $questionName = new Question('Enter the queue (tube) name to use [php-censor-queue]: ', 'php-censor-queue'); - $rtn['queue'] = $helper->ask($input, $output, $questionName); + $rtn['name'] = $helper->ask($input, $output, $questionName); } return $rtn; diff --git a/src/PHPCensor/Command/WorkerCommand.php b/src/PHPCensor/Command/WorkerCommand.php index 323e8807..a211d63e 100644 --- a/src/PHPCensor/Command/WorkerCommand.php +++ b/src/PHPCensor/Command/WorkerCommand.php @@ -73,16 +73,15 @@ class WorkerCommand extends Command define('DEBUG_MODE', true); } - $config = Config::getInstance()->get('php-censor.worker', []); + $config = Config::getInstance()->get('php-censor.queue', []); - if (empty($config['host']) || empty($config['queue'])) { + if (empty($config['host']) || empty($config['name'])) { $error = 'The worker is not configured. You must set a host and queue in your config.yml file.'; throw new \Exception($error); } - $worker = new BuildWorker($config['host'], $config['queue']); + $worker = new BuildWorker($config['host'], $config['name']); $worker->setLogger($this->logger); - $worker->setMaxJobs(Config::getInstance()->get('php-censor.worker.max_jobs', -1)); $worker->startWorker(); } } diff --git a/src/PHPCensor/Controller/SettingsController.php b/src/PHPCensor/Controller/SettingsController.php deleted file mode 100644 index 5e274405..00000000 --- a/src/PHPCensor/Controller/SettingsController.php +++ /dev/null @@ -1,498 +0,0 @@ - - * @package PHPCI - * @subpackage Web - */ -class SettingsController extends Controller -{ - - /** - * @var array - */ - protected $settings; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - parent::init(); - - $parser = new Parser(); - $yaml = file_get_contents(APP_DIR . 'config.yml'); - $this->settings = $parser->parse($yaml); - } - - /** - * Display settings forms. - * - * @return string - */ - public function index() - { - $this->requireAdmin(); - - $this->layout->title = Lang::get('settings'); - - $this->view->settings = $this->settings; - - $basicSettings = []; - if (isset($this->settings['php-censor']['basic'])) { - $basicSettings = $this->settings['php-censor']['basic']; - } - - $buildSettings = []; - if (isset($this->settings['php-censor']['build'])) { - $buildSettings = $this->settings['php-censor']['build']; - } - - $emailSettings = []; - if (isset($this->settings['php-censor']['email_settings'])) { - $emailSettings = $this->settings['php-censor']['email_settings']; - } - - $securitySettings = []; - if (isset($this->settings['php-censor']['security'])) { - $securitySettings = $this->settings['php-censor']['security']; - } - - $this->view->configFile = APP_DIR . 'config.yml'; - $this->view->basicSettings = $this->getBasicForm($basicSettings); - $this->view->buildSettings = $this->getBuildForm($buildSettings); - $this->view->github = $this->getGithubForm(); - $this->view->emailSettings = $this->getEmailForm($emailSettings); - $this->view->securitySettings = $this->getAuthenticationForm($securitySettings); - $this->view->isWriteable = $this->canWriteConfig(); - - if (!empty($this->settings['php-censor']['github']['token'])) { - $this->view->githubUser = $this->getGithubUser($this->settings['php-censor']['github']['token']); - } - - return $this->view->render(); - } - - /** - * Save Github settings. - */ - public function github() - { - $this->requireAdmin(); - - $this->settings['php-censor']['github']['id'] = $this->getParam('githubid', ''); - $this->settings['php-censor']['github']['secret'] = $this->getParam('githubsecret', ''); - $error = $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - - if ($error) { - $response->setHeader('Location', APP_URL . 'settings?saved=2'); - } else { - $response->setHeader('Location', APP_URL . 'settings?saved=1'); - } - - return $response; - } - - /** - * Save email settings. - */ - public function email() - { - $this->requireAdmin(); - - $this->settings['php-censor']['email_settings'] = $this->getParams(); - $this->settings['php-censor']['email_settings']['smtp_encryption'] = $this->getParam('smtp_encryption', 0); - - $error = $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - - if ($error) { - $response->setHeader('Location', APP_URL . 'settings?saved=2'); - } else { - $response->setHeader('Location', APP_URL . 'settings?saved=1'); - } - - return $response; - } - - /** - * Save build settings. - */ - public function build() - { - $this->requireAdmin(); - - $this->settings['php-censor']['build'] = $this->getParams(); - - $error = $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - - if ($error) { - $response->setHeader('Location', APP_URL . 'settings?saved=2'); - } else { - $response->setHeader('Location', APP_URL . 'settings?saved=1'); - } - - return $response; - } - - /** - * Save basic settings. - */ - public function basic() - { - $this->requireAdmin(); - - $this->settings['php-censor']['basic'] = $this->getParams(); - $error = $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - - if ($error) { - $response->setHeader('Location', APP_URL . 'settings?saved=2'); - } else { - $response->setHeader('Location', APP_URL . 'settings?saved=1'); - } - - return $response; - } - - /** - * Handle authentication settings - */ - public function authentication() - { - $this->requireAdmin(); - - $this->settings['php-censor']['security']['disable_auth'] = (boolean)$this->getParam('disable_authentication', false); - $this->settings['php-censor']['security']['default_user_id'] = (integer)$_SESSION['php-censor-user-id']; - - $error = $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - - if ($error) { - $response->setHeader('Location', APP_URL . 'settings?saved=2'); - } else { - $response->setHeader('Location', APP_URL . 'settings?saved=1'); - } - - return $response; - } - - /** - * Github redirects users back to this URL when t - */ - public function githubCallback() - { - $code = $this->getParam('code', null); - $github = $this->settings['php-censor']['github']; - - if (!is_null($code)) { - $http = new HttpClient(); - $url = 'https://github.com/login/oauth/access_token'; - $params = ['client_id' => $github['id'], 'client_secret' => $github['secret'], 'code' => $code]; - $resp = $http->post($url, $params); - - if ($resp['success']) { - parse_str($resp['body'], $resp); - - $this->settings['php-censor']['github']['token'] = $resp['access_token']; - $this->storeSettings(); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL . 'settings?linked=1'); - return $response; - } - } - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL . 'settings?linked=2'); - return $response; - } - - /** - * Convert config to yaml and store to file. - * - * @return mixed - */ - protected function storeSettings() - { - $dumper = new Dumper(); - $yaml = $dumper->dump($this->settings, 4); - file_put_contents(APP_DIR . 'config.yml', $yaml); - - if (error_get_last()) { - $error_get_last = error_get_last(); - return $error_get_last['message']; - } - } - - /** - * Get the Github settings form. - * @return Form - */ - protected function getGithubForm() - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'settings/github'); - $form->addField(new Form\Element\Csrf('csrf')); - - $field = new Form\Element\Text('githubid'); - $field->setRequired(true); - $field->setPattern('[a-zA-Z0-9]+'); - $field->setLabel(Lang::get('application_id')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - if (isset($this->settings['php-censor']['github']['id'])) { - $field->setValue($this->settings['php-censor']['github']['id']); - } - - $field = new Form\Element\Text('githubsecret'); - $field->setRequired(true); - $field->setPattern('[a-zA-Z0-9]+'); - $field->setLabel(Lang::get('application_secret')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - if (isset($this->settings['php-censor']['github']['secret'])) { - $field->setValue($this->settings['php-censor']['github']['secret']); - } - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save')); - $field->setClass('btn btn-success pull-right'); - $form->addField($field); - - return $form; - } - - /** - * Get the email settings form. - * @param array $values - * @return Form - */ - protected function getEmailForm($values = []) - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'settings/email'); - $form->addField(new Form\Element\Csrf('csrf')); - - $field = new Form\Element\Text('smtp_address'); - $field->setRequired(false); - $field->setLabel(Lang::get('smtp_server')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $field->setValue('localhost'); - $form->addField($field); - - $field = new Form\Element\Text('smtp_port'); - $field->setRequired(false); - $field->setPattern('[0-9]+'); - $field->setLabel(Lang::get('smtp_port')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $field->setValue(25); - $form->addField($field); - - $field = new Form\Element\Text('smtp_username'); - $field->setRequired(false); - $field->setLabel(Lang::get('smtp_username')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Password('smtp_password'); - $field->setRequired(false); - $field->setLabel(Lang::get('smtp_password')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Email('from_address'); - $field->setRequired(false); - $field->setLabel(Lang::get('from_email_address')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Email('default_mailto_address'); - $field->setRequired(false); - $field->setLabel(Lang::get('default_notification_address')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Select('smtp_encryption'); - $field->setOptions(['' => Lang::get('none'), 'tls' => Lang::get('tls'), 'ssl' => Lang::get('ssl')]); - $field->setRequired(false); - $field->setLabel(Lang::get('use_smtp_encryption')); - $field->setContainerClass('form-group'); - $field->setValue(1); - $form->addField($field); - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save')); - $field->setClass('btn btn-success pull-right'); - $form->addField($field); - - $form->setValues($values); - - return $form; - } - - /** - * Call Github API for our Github user object. - * @param $token - * @return mixed - */ - protected function getGithubUser($token) - { - $http = new HttpClient('https://api.github.com'); - $user = $http->get('/user', ['access_token' => $token]); - - return $user['body']; - } - - /** - * Check if we can write the PHPCI config file. - * @return bool - */ - protected function canWriteConfig() - { - return is_writeable(APP_DIR . 'config.yml'); - } - - /** - * Get the Build settings form. - * @param array $values - * @return Form - */ - protected function getBuildForm($values = []) - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'settings/build'); - - $field = new Form\Element\Select('failed_after'); - $field->setRequired(false); - $field->setLabel(Lang::get('failed_after')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $field->setOptions([ - 300 => Lang::get('5_mins'), - 900 => Lang::get('15_mins'), - 1800 => Lang::get('30_mins'), - 3600 => Lang::get('1_hour'), - 10800 => Lang::get('3_hours'), - ]); - $field->setValue(1800); - $form->addField($field); - - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save')); - $field->setClass('btn btn-success pull-right'); - $form->addField($field); - - $form->setValues($values); - - return $form; - } - - /** - * Get the Basic settings form. - * @param array $values - * @return Form - */ - protected function getBasicForm($values = []) - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'settings/basic'); - - $field = new Form\Element\Select('language'); - $field->setRequired(true); - $field->setLabel(Lang::get('language')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $field->setOptions(Lang::getLanguageOptions()); - $field->setValue(Lang::getLanguage()); - $form->addField($field); - - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save')); - $field->setClass('btn btn-success pull-right'); - $form->addField($field); - - $form->setValues($values); - - return $form; - } - - /** - * Form for disabling user authentication while using a default user - * - * @param array $values - * @return Form - */ - protected function getAuthenticationForm($values = []) - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'settings/authentication'); - $form->addField(new Form\Element\Csrf('csrf')); - - $field = new Form\Element\Checkbox('disable_authentication'); - $field->setCheckedValue(1); - $field->setRequired(false); - $field->setLabel('Disable Authentication?'); - $field->setContainerClass('form-group'); - $field->setValue(0); - - if (isset($values['disable_auth'])) { - $field->setValue((integer)$values['disable_auth']); - } - - $form->addField($field); - - $field = new Form\Element\Submit(); - $field->setValue('Save »'); - $field->setClass('btn btn-success pull-right'); - $form->addField($field); - - $form->setValues($values); - - return $form; - } -} diff --git a/src/PHPCensor/Service/BuildService.php b/src/PHPCensor/Service/BuildService.php index 2ac88557..aaf5a85b 100644 --- a/src/PHPCensor/Service/BuildService.php +++ b/src/PHPCensor/Service/BuildService.php @@ -160,9 +160,9 @@ class BuildService } $config = Config::getInstance(); - $settings = $config->get('php-censor.worker', []); + $settings = $config->get('php-censor.queue', []); - if (!empty($settings['host']) && !empty($settings['queue'])) { + if (!empty($settings['host']) && !empty($settings['name'])) { try { $jobData = [ 'type' => 'php-censor.build', @@ -174,12 +174,12 @@ class BuildService } $pheanstalk = new Pheanstalk($settings['host']); - $pheanstalk->useTube($settings['queue']); + $pheanstalk->useTube($settings['name']); $pheanstalk->put( json_encode($jobData), PheanstalkInterface::DEFAULT_PRIORITY, PheanstalkInterface::DEFAULT_DELAY, - $config->get('php-censor.worker.job_timeout', 600) + $config->get('php-censor.queue.lifetime', 600) ); } catch (\Exception $ex) { $this->queueError = true; diff --git a/src/PHPCensor/View/layout.phtml b/src/PHPCensor/View/layout.phtml index 709880f4..19cddb47 100644 --- a/src/PHPCensor/View/layout.phtml +++ b/src/PHPCensor/View/layout.phtml @@ -143,26 +143,17 @@ -