Merge branch 'master' of git://github.com/Block8/PHPCI into feature/summaryview
This commit is contained in:
commit
d02e5d86d0
|
@ -10,6 +10,7 @@
|
|||
namespace PHPCI;
|
||||
|
||||
use b8;
|
||||
use b8\Registry;
|
||||
use b8\Http\Response\RedirectResponse;
|
||||
use b8\View;
|
||||
|
||||
|
@ -33,8 +34,11 @@ class Application extends b8\Application
|
|||
$sessionAction = ($this->controllerName == 'Session' && in_array($this->action, array('login', 'logout')));
|
||||
$externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'BuildStatus'));
|
||||
$skipValidation = ($externalAction || $sessionAction);
|
||||
|
||||
|
||||
if($skipValidation || $this->validateSession()) {
|
||||
if ( !empty($_SESSION['user']) ) {
|
||||
Registry::getInstance()->set('user', $_SESSION['user']);
|
||||
}
|
||||
parent::handleRequest();
|
||||
}
|
||||
|
||||
|
@ -43,7 +47,7 @@ class Application extends b8\Application
|
|||
$view->content = $this->response->getContent();
|
||||
$this->response->setContent($view->render());
|
||||
}
|
||||
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,16 @@ class Builder
|
|||
return isset($this->config[$key]) ? $this->config[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a variable from the config.yml
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSystemConfig($key)
|
||||
{
|
||||
return \b8\Registry::getInstance()->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the build.
|
||||
* @param Build
|
||||
|
@ -125,6 +135,22 @@ class Builder
|
|||
return $this->build;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The title of the project being built.
|
||||
*/
|
||||
public function getBuildProjectTitle() {
|
||||
return $this->getBuild()->getProject()->getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the build has passed or failed.
|
||||
* @return bool
|
||||
*/
|
||||
public function getSuccessStatus()
|
||||
{
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the active build.
|
||||
*/
|
||||
|
|
|
@ -48,6 +48,13 @@ class InstallCommand extends Command
|
|||
$conf['phpci']['github']['id'] = $this->ask('(Optional) Github Application ID: ', true);
|
||||
$conf['phpci']['github']['secret'] = $this->ask('(Optional) Github Application Secret: ', true);
|
||||
|
||||
$conf['phpci']['email_settings']['smtp_address'] = $this->ask('(Optional) Smtp server address: ', true);
|
||||
$conf['phpci']['email_settings']['smtp_port'] = $this->ask('(Optional) Smtp port: ', true);
|
||||
$conf['phpci']['email_settings']['smtp_username'] = $this->ask('(Optional) Smtp Username: ', true);
|
||||
$conf['phpci']['email_settings']['smtp_password'] = $this->ask('(Optional) Smtp Password: ', true);
|
||||
$conf['phpci']['email_settings']['from_address'] = $this->ask('(Optional) Email address to send from: ', true);
|
||||
$conf['phpci']['email_settings']['default_mailto_address'] = $this->ask('(Optional) Default address to email notifications to: ', true);
|
||||
|
||||
$dbUser = $conf['b8']['database']['username'];
|
||||
$dbPass = $conf['b8']['database']['password'];
|
||||
$dbHost = $conf['b8']['database']['servers']['write'];
|
||||
|
|
|
@ -86,10 +86,10 @@ class BuildController extends \PHPCI\Controller
|
|||
*/
|
||||
public function delete($buildId)
|
||||
{
|
||||
if (!Registry::getInstance()->get('user')->getIsAdmin()) {
|
||||
if (empty($_SESSION['user']) || !$_SESSION['user']->getIsAdmin()) {
|
||||
throw new \Exception('You do not have permission to do that.');
|
||||
}
|
||||
|
||||
|
||||
$build = $this->_buildStore->getById($buildId);
|
||||
$this->_buildStore->delete($build);
|
||||
|
||||
|
|
191
PHPCI/Plugin/Email.php
Normal file
191
PHPCI/Plugin/Email.php
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2013, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link http://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCI\Plugin;
|
||||
|
||||
|
||||
/**
|
||||
* Email Plugin - Provides simple email capability to PHPCI.
|
||||
* @author Steve Brazier <meadsteve@gmail.com>
|
||||
* @package PHPCI
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Email implements \PHPCI\Plugin
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Builder
|
||||
*/
|
||||
protected $phpci;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $emailConfig;
|
||||
|
||||
/**
|
||||
* @var \Swift_Mailer
|
||||
*/
|
||||
protected $mailer;
|
||||
|
||||
public function __construct(\PHPCI\Builder $phpci,
|
||||
array $options = array(),
|
||||
\Swift_Mailer $mailer = null)
|
||||
{
|
||||
$phpCiSettings = $phpci->getSystemConfig('phpci');
|
||||
$this->phpci = $phpci;
|
||||
$this->options = $options;
|
||||
$this->emailConfig = isset($phpCiSettings['email_settings']) ? $phpCiSettings['email_settings'] : array();
|
||||
|
||||
// Either a mailer will have been passed in or we load from the
|
||||
// config.
|
||||
if ($mailer === null) {
|
||||
$this->loadSwiftMailerFromConfig();
|
||||
}
|
||||
else {
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to MySQL and runs a specified set of queries.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$addresses = $this->getEmailAddresses();
|
||||
|
||||
// Without some email addresses in the yml file then we
|
||||
// can't do anything.
|
||||
if (count($addresses) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sendFailures = array();
|
||||
|
||||
$subjectTemplate = "PHPCI - %s - %s";
|
||||
$projectName = $this->phpci->getBuildProjectTitle();
|
||||
$logText = $this->phpci->getBuild()->getLog();
|
||||
|
||||
if($this->phpci->getSuccessStatus()) {
|
||||
$sendFailures = $this->sendSeparateEmails(
|
||||
$addresses,
|
||||
sprintf($subjectTemplate, $projectName, "Passing Build"),
|
||||
sprintf("Log Output: <br><pre>%s</pre>", $logText)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$sendFailures = $this->sendSeparateEmails(
|
||||
$addresses,
|
||||
sprintf($subjectTemplate, $projectName, "Failing Build"),
|
||||
sprintf("Log Output: <br><pre>%s</pre>", $logText)
|
||||
);
|
||||
}
|
||||
|
||||
// This is a success if we've not failed to send anything.
|
||||
$this->phpci->log(sprintf(
|
||||
"%d emails sent",
|
||||
(count($addresses) - count($sendFailures)))
|
||||
);
|
||||
$this->phpci->log(sprintf(
|
||||
"%d emails failed to send",
|
||||
count($sendFailures))
|
||||
);
|
||||
return (count($sendFailures) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $toAddresses Array or single address to send to
|
||||
* @param string $subject Email subject
|
||||
* @param string $body Email body
|
||||
* @return array Array of failed addresses
|
||||
*/
|
||||
public function sendEmail($toAddresses, $subject, $body)
|
||||
{
|
||||
$message = \Swift_Message::newInstance($subject)
|
||||
->setFrom($this->getMailConfig('from_address'))
|
||||
->setTo($toAddresses)
|
||||
->setBody($body)
|
||||
->setContentType("text/html");
|
||||
$failedAddresses = array();
|
||||
$this->mailer->send($message, $failedAddresses);
|
||||
|
||||
return $failedAddresses;
|
||||
}
|
||||
|
||||
public function sendSeparateEmails(array $toAddresses, $subject, $body)
|
||||
{
|
||||
$failures = array();
|
||||
foreach($toAddresses as $address) {
|
||||
$newFailures = $this->sendEmail($address, $subject, $body);
|
||||
foreach($newFailures as $failure) {
|
||||
$failures[] = $failure;
|
||||
}
|
||||
}
|
||||
return $failures;
|
||||
}
|
||||
|
||||
protected function loadSwiftMailerFromConfig()
|
||||
{
|
||||
/** @var \Swift_SmtpTransport $transport */
|
||||
$transport = \Swift_SmtpTransport::newInstance(
|
||||
$this->getMailConfig('smtp_address'),
|
||||
$this->getMailConfig('smtp_port')
|
||||
);
|
||||
$transport->setUsername($this->getMailConfig('smtp_username'));
|
||||
$transport->setPassword($this->getMailConfig('smtp_password'));
|
||||
|
||||
$this->mailer = \Swift_Mailer::newInstance($transport);
|
||||
}
|
||||
|
||||
protected function getMailConfig($configName)
|
||||
{
|
||||
if (isset($this->emailConfig[$configName])
|
||||
&& $this->emailConfig[$configName] != "")
|
||||
{
|
||||
return $this->emailConfig[$configName];
|
||||
}
|
||||
// Check defaults
|
||||
else {
|
||||
switch($configName) {
|
||||
case 'smtp_address':
|
||||
return "localhost";
|
||||
case 'default_mailto_address':
|
||||
return null;
|
||||
case 'smtp_port':
|
||||
return '25';
|
||||
case 'from_address':
|
||||
return "notifications-ci@phptesting.org";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEmailAddresses()
|
||||
{
|
||||
$addresses = array();
|
||||
|
||||
if (isset($this->options['addresses'])) {
|
||||
foreach ($this->options['addresses'] as $address) {
|
||||
$addresses[] = $address;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->options['default_mailto_address'])) {
|
||||
$addresses[] = $this->options['default_mailto_address'];
|
||||
return $addresses;
|
||||
}
|
||||
return $addresses;
|
||||
}
|
||||
}
|
|
@ -11,21 +11,26 @@ switch($build->getStatus())
|
|||
{
|
||||
case 0:
|
||||
$cls = 'info';
|
||||
$subcls = 'info';
|
||||
$status = 'Pending';
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$cls = 'warning';
|
||||
$subcls = 'warning';
|
||||
$status = 'Running';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$cls = 'success';
|
||||
$subcls = 'success';
|
||||
$status = 'Success';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$cls = 'error';
|
||||
$subcls = 'important';
|
||||
$status = 'Failed';
|
||||
break;
|
||||
}
|
||||
|
@ -35,7 +40,31 @@ switch($build->getStatus())
|
|||
<td><a href="<?= PHPCI_URL ?>project/view/<?php print $build->getProjectId(); ?>"><?php print $build->getProject()->getTitle(); ?></a></td>
|
||||
<td><a href="<?php print $build->getCommitLink(); ?>"><?php print $build->getCommitId(); ?></a></td>
|
||||
<td><a href="<?php print $build->getBranchLink(); ?>"><?php print $build->getBranch(); ?></a></td>
|
||||
<td><?php print $status; ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$plugins = json_decode($build->getPlugins(), true);
|
||||
|
||||
if ( !is_array($plugins) ) {
|
||||
$plugins = array();
|
||||
}
|
||||
if ( 0 === count($plugins) ) {
|
||||
?>
|
||||
<span class='label label-<?= $subcls ?>'>
|
||||
<?= $status ?>
|
||||
</span>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
foreach($plugins as $plugin => $pluginstatus):
|
||||
$subcls = $pluginstatus?'label label-success':'label label-important';
|
||||
?>
|
||||
<span class='<?= $subcls ?>'>
|
||||
<?= ucwords(str_replace('_', ' ', $plugin)) ?>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
<br style='clear:both;' />
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<a class="btn" href="<?= PHPCI_URL ?>build/view/<?php print $build->getId(); ?>">View</a>
|
||||
|
|
|
@ -61,14 +61,14 @@
|
|||
$pages = ceil($total / 10);
|
||||
$pages = $pages == 0 ? 1 : $pages;
|
||||
|
||||
print '<li class="'.($page == 1 ? 'disabled' : '').'"><a href="<?= PHPCI_URL ?>project/view/'.$project->getId().'?p='.($page == 1 ? '1' : $page - 1).'">«</a></li>';
|
||||
print '<li class="'.($page == 1 ? 'disabled' : '').'"><a href="' . PHPCI_URL . 'project/view/'.$project->getId().'?p='.($page == 1 ? '1' : $page - 1).'">«</a></li>';
|
||||
|
||||
for($i = 1; $i <= $pages; $i++)
|
||||
{
|
||||
print '<li><a href="<?= PHPCI_URL ?>project/view/' . $project->getId() . '?p=' . $i . '">' . $i . '</a></li>';
|
||||
print '<li><a href="' . PHPCI_URL . 'project/view/' . $project->getId() . '?p=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
|
||||
print '<li class="'.($page == $pages ? 'disabled' : '').'"><a href="<?= PHPCI_URL ?>project/view/'.$project->getId().'?p='.($page == $pages ? $pages : $page + 1).'">»</a></li>';
|
||||
print '<li class="'.($page == $pages ? 'disabled' : '').'"><a href="' . PHPCI_URL . 'project/view/'.$project->getId().'?p='.($page == $pages ? $pages : $page + 1).'">»</a></li>';
|
||||
|
||||
print '</ul></div>';
|
||||
|
||||
|
|
256
Tests/PHPCI/Plugin/Email.php
Normal file
256
Tests/PHPCI/Plugin/Email.php
Normal file
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
/**
|
||||
* PHPCI - Continuous Integration for PHP
|
||||
*
|
||||
* @copyright Copyright 2013, Block 8 Limited.
|
||||
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
|
||||
* @link http://www.phptesting.org/
|
||||
*/
|
||||
|
||||
namespace PHPCI\Plugin\Tests;
|
||||
use PHPCI\Plugin\Email as EmailPlugin;
|
||||
|
||||
define('PHPCI_BIN_DIR', "FAKEPHPCIBIN");
|
||||
|
||||
/**
|
||||
* Unit test for the PHPUnit plugin.
|
||||
* @author meadsteve
|
||||
*/
|
||||
class EmailTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var EmailPlugin $testedPhpUnit
|
||||
*/
|
||||
protected $testedEmailPlugin;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject $mockCiBuilder
|
||||
*/
|
||||
protected $mockCiBuilder;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject $mockMailer
|
||||
*/
|
||||
protected $mockMailer;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject $mockMailer
|
||||
*/
|
||||
protected $mockBuild;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->mockBuild = $this->getMock(
|
||||
'\PHPCI\Model\Build',
|
||||
array('getLog'),
|
||||
array(),
|
||||
"mockBuild",
|
||||
false
|
||||
);
|
||||
|
||||
$this->mockBuild->expects($this->any())
|
||||
->method('getLog')
|
||||
->will($this->returnValue("Build Log"));
|
||||
|
||||
$this->mockCiBuilder = $this->getMock(
|
||||
'\PHPCI\Builder',
|
||||
array('getSystemConfig',
|
||||
'getBuildProjectTitle',
|
||||
'getBuild',
|
||||
'log'),
|
||||
array(),
|
||||
"mockBuilder",
|
||||
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"
|
||||
)
|
||||
)));
|
||||
$this->mockCiBuilder->expects($this->any())
|
||||
->method('getBuildProjectTitle')
|
||||
->will($this->returnValue('Test-Project'));
|
||||
$this->mockCiBuilder->expects($this->any())
|
||||
->method('getBuild')
|
||||
->will($this->returnValue($this->mockBuild));
|
||||
|
||||
$this->mockMailer = $this->getMock(
|
||||
'\Swift_Mailer',
|
||||
array('send'),
|
||||
array(),
|
||||
"mockMailer",
|
||||
false
|
||||
);
|
||||
|
||||
$this->loadEmailPluginWithOptions();
|
||||
}
|
||||
|
||||
protected function loadEmailPluginWithOptions($arrOptions = array())
|
||||
{
|
||||
$this->testedEmailPlugin = new EmailPlugin(
|
||||
$this->mockCiBuilder,
|
||||
$arrOptions,
|
||||
$this->mockMailer
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PHPUnit::execute
|
||||
*/
|
||||
public function testExecute_ReturnsFalseWithoutArgs()
|
||||
{
|
||||
$returnValue = $this->testedEmailPlugin->execute();
|
||||
// As no addresses will have been mailed as non are configured.
|
||||
$expectedReturn = false;
|
||||
|
||||
$this->assertEquals($expectedReturn, $returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
$this->assertSystemMail(
|
||||
'test-receiver@example.com',
|
||||
'test-from-address@example.com',
|
||||
"Log Output: <br><pre>Build Log</pre>",
|
||||
"PHPCI - Test-Project - Passing Build",
|
||||
$actualMail
|
||||
);
|
||||
|
||||
$this->assertEquals($expectedReturn, $returnValue);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PHPUnit::sendEmail
|
||||
*/
|
||||
public function testSendEmail_CallsMailerSend()
|
||||
{
|
||||
$this->mockMailer->expects($this->once())
|
||||
->method('send');
|
||||
$this->testedEmailPlugin->sendEmail("test@email.com", "hello", "body");
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PHPUnit::sendEmail
|
||||
*/
|
||||
public function testSendEmail_BuildsAMessageObject()
|
||||
{
|
||||
$subject = "Test mail";
|
||||
$body = "Message Body";
|
||||
$toAddress = "test@example.com";
|
||||
|
||||
$this->mockMailer->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->isInstanceOf('\Swift_Message'), $this->anything());
|
||||
$this->testedEmailPlugin->sendEmail($toAddress, $subject, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PHPUnit::sendEmail
|
||||
*/
|
||||
public function testSendEmail_BuildsExpectedMessage()
|
||||
{
|
||||
$subject = "Test mail";
|
||||
$body = "Message Body";
|
||||
$toAddress = "test@example.com";
|
||||
$expectedMessage = \Swift_Message::newInstance($subject)
|
||||
->setFrom('test-from-address@example.com')
|
||||
->setTo($toAddress)
|
||||
->setBody($body);
|
||||
|
||||
/** @var \Swift_Message $actualMail */
|
||||
$actualMail = null;
|
||||
$this->catchMailPassedToSend($actualMail);
|
||||
|
||||
$this->testedEmailPlugin->sendEmail($toAddress, $subject, $body);
|
||||
|
||||
$this->assertSystemMail(
|
||||
$toAddress,
|
||||
'test-from-address@example.com',
|
||||
$body,
|
||||
$subject,
|
||||
$actualMail
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Swift_Message $actualMail passed by ref and populated with
|
||||
* the message object the mock mailer
|
||||
* receives.
|
||||
*/
|
||||
protected function catchMailPassedToSend(&$actualMail)
|
||||
{
|
||||
$this->mockMailer->expects($this->once())
|
||||
->method('send')
|
||||
->will(
|
||||
$this->returnCallback(
|
||||
function ($passedMail) use (&$actualMail) {
|
||||
$actualMail = $passedMail;
|
||||
return array();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the actual mail object is populated as expected.
|
||||
*
|
||||
* @param string $expectedToAddress
|
||||
* @param $expectedFromAddress
|
||||
* @param string $expectedBody
|
||||
* @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);
|
||||
throw new \Exception("Expected Swift_Message got " . $type);
|
||||
}
|
||||
$this->assertEquals(
|
||||
array($expectedFromAddress => null),
|
||||
$actualMail->getFrom()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array($expectedToAddress => null),
|
||||
$actualMail->getTo()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$expectedBody,
|
||||
$actualMail->getBody()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$expectedSubject,
|
||||
$actualMail->getSubject()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,20 @@ body
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
td .label { margin-right: 5px; }
|
||||
|
||||
.success-message {
|
||||
background-color: #4F8A10;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: #FF4747;
|
||||
}
|
||||
|
||||
#latest-builds td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.widget-title, .modal-header, .table th, div.dataTables_wrapper .ui-widget-header, .ui-dialog .ui-dialog-titlebar {
|
||||
background-color: #efefef;
|
||||
background-image: -webkit-gradient(linear, 0 0%, 0 100%, from(#fdfdfd), to(#eaeaea));
|
||||
|
@ -61,7 +75,7 @@ body
|
|||
background: url('/assets/img/icon-build-running.png') no-repeat top left;
|
||||
}
|
||||
|
||||
h3
|
||||
h3
|
||||
{
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-top: 0;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"phpspec/phpspec" : "2.*",
|
||||
"symfony/yaml" : "2.2.x-dev",
|
||||
"symfony/console" : "2.2.*",
|
||||
"fabpot/php-cs-fixer" : "0.3.*@dev"
|
||||
"fabpot/php-cs-fixer" : "0.3.*@dev",
|
||||
"swiftmailer/swiftmailer" : "v5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue