Merge branch 'master' into phplint-output-block

This commit is contained in:
Stephen Ball 2015-05-28 11:25:57 +01:00
commit a1d240faa4
132 changed files with 4388 additions and 1039 deletions

View file

@ -215,15 +215,6 @@ class Builder implements LoggerAwareInterface
$this->pluginExecutor->executePlugins($this->config, 'failure');
$this->buildLogger->logFailure(Lang::get('build_failed'));
}
// Clean up:
$this->buildLogger->log(Lang::get('removing_build'));
$cmd = 'rm -Rf "%s"';
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
$this->executeCommand($cmd, rtrim($this->buildPath, '/'));
} catch (\Exception $ex) {
$this->build->setStatus(Build::STATUS_FAILED);
$this->buildLogger->logFailure(Lang::get('exception') . $ex->getMessage());
@ -233,6 +224,11 @@ class Builder implements LoggerAwareInterface
// Update the build in the database, ping any external services, etc.
$this->build->sendStatusPostback();
$this->build->setFinished(new \DateTime());
// Clean up:
$this->buildLogger->log(Lang::get('removing_build'));
$this->build->removeBuildDirectory();
$this->store->save($this->build);
}
@ -263,12 +259,14 @@ class Builder implements LoggerAwareInterface
/**
* Find a binary required by a plugin.
* @param $binary
* @param string $binary
* @param bool $quiet
*
* @return null|string
*/
public function findBinary($binary)
public function findBinary($binary, $quiet = false)
{
return $this->commandExecutor->findBinary($binary, $this->buildPath);
return $this->commandExecutor->findBinary($binary, $quiet = false);
}
/**
@ -287,7 +285,7 @@ class Builder implements LoggerAwareInterface
*/
protected function setupBuild()
{
$this->buildPath = PHPCI_DIR . 'PHPCI/build/' . $this->build->getId() . '/';
$this->buildPath = $this->build->getBuildPath() . '/';
$this->build->currentBuildPath = $this->buildPath;
$this->interpolator->setupInterpolationVars(

View file

@ -0,0 +1,85 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Command;
use PHPCI\Helper\Lang;
use PHPCI\Service\BuildService;
use PHPCI\Store\ProjectStore;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Create build command - creates a build for a project
* @author Jérémy DECOOL (@jdecool)
* @package PHPCI
* @subpackage Console
*/
class CreateBuildCommand extends Command
{
/**
* @var ProjectStore
*/
protected $projectStore;
/**
* @var BuildService
*/
protected $buildService;
/**
* @param ProjectStore $projectStore
*/
public function __construct(ProjectStore $projectStore, BuildService $buildService)
{
parent::__construct();
$this->projectStore = $projectStore;
$this->buildService = $buildService;
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('phpci:create-build')
->setDescription(Lang::get('create_build_project'))
->addArgument('projectId', InputArgument::REQUIRED, Lang::get('project_id_argument'))
->addOption('commit', null, InputOption::VALUE_OPTIONAL, Lang::get('commit_id_option'))
->addOption('branch', null, InputOption::VALUE_OPTIONAL, Lang::get('branch_name_option'));
}
/**
* {@inheritDoc}
*/
public function execute(InputInterface $input, OutputInterface $output)
{
$projectId = $input->getArgument('projectId');
$commitId = $input->getOption('commit');
$branch = $input->getOption('branch');
$project = $this->projectStore->getById($projectId);
if (empty($project)) {
throw new \InvalidArgumentException('Project does not exist: ' . $projectId);
}
try {
$this->buildService->createBuild($project, $commitId, $branch);
$output->writeln(Lang::get('build_created'));
} catch (\Exception $e) {
$output->writeln(sprintf('<error>%s</error>', Lang::get('failed')));
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
}
}
}

View file

@ -1,4 +1,5 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
@ -10,28 +11,48 @@
namespace PHPCI\Command;
use Monolog\Logger;
use PHPCI\ProcessControl\Factory;
use PHPCI\ProcessControl\ProcessControlInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Daemon that loops and call the run-command.
* @author Gabriel Baker <gabriel.baker@autonomicpilot.co.uk>
* @package PHPCI
* @subpackage Console
*/
* Daemon that loops and call the run-command.
* @author Gabriel Baker <gabriel.baker@autonomicpilot.co.uk>
* @package PHPCI
* @subpackage Console
*/
class DaemonCommand extends Command
{
/**
* @var \Monolog\Logger
* @var Logger
*/
protected $logger;
public function __construct(Logger $logger, $name = null)
/**
* @var string
*/
protected $pidFilePath;
/**
* @var string
*/
protected $logFilePath;
/**
* @var ProcessControlInterface
*/
protected $processControl;
public function __construct(Logger $logger, ProcessControlInterface $processControl = null, $name = null)
{
parent::__construct($name);
$this->logger = $logger;
$this->processControl = $processControl ?: Factory::getInstance();
}
protected function configure()
@ -40,17 +61,30 @@ class DaemonCommand extends Command
->setName('phpci:daemon')
->setDescription('Initiates the daemon to run commands.')
->addArgument(
'state',
InputArgument::REQUIRED,
'start|stop|status'
);
'state', InputArgument::REQUIRED, 'start|stop|status'
)
->addOption(
'pid-file', 'p', InputOption::VALUE_REQUIRED,
'Path of the PID file',
implode(DIRECTORY_SEPARATOR,
array(PHPCI_DIR, 'daemon', 'daemon.pid'))
)
->addOption(
'log-file', 'l', InputOption::VALUE_REQUIRED,
'Path of the log file',
implode(DIRECTORY_SEPARATOR,
array(PHPCI_DIR, 'daemon', 'daemon.log'))
);
}
/**
* Loops through running.
*/
* Loops through running.
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->pidFilePath = $input->getOption('pid-file');
$this->logFilePath = $input->getOption('log-file');
$state = $input->getArgument('state');
switch ($state) {
@ -61,64 +95,108 @@ class DaemonCommand extends Command
$this->stopDaemon();
break;
case 'status':
$this->statusDaemon();
$this->statusDaemon($output);
break;
default:
echo "Not a valid choice, please use start stop or status";
$this->output->writeln("<error>Not a valid choice, please use start, stop or status</error>");
break;
}
}
protected function startDaemon()
{
if (file_exists(PHPCI_DIR.'/daemon/daemon.pid')) {
echo "Already started\n";
$this->logger->warning("Daemon already started");
$pid = $this->getRunningPid();
if ($pid) {
$this->logger->notice("Daemon already started", array('pid' => $pid));
return "alreadystarted";
}
$logfile = PHPCI_DIR."/daemon/daemon.log";
$this->logger->info("Trying to start the daemon");
$cmd = "nohup %s/daemonise phpci:daemonise > %s 2>&1 &";
$command = sprintf($cmd, PHPCI_DIR, $logfile);
$this->logger->info("Daemon started");
exec($command);
$command = sprintf($cmd, PHPCI_DIR, $this->logFilePath);
$output = $exitCode = null;
exec($command, $output, $exitCode);
if ($exitCode !== 0) {
$this->logger->error(sprintf("daemonise exited with status %d", $exitCode));
return "notstarted";
}
for ($i = 0; !($pid = $this->getRunningPid()) && $i < 5; $i++) {
sleep(1);
}
if (!$pid) {
$this->logger->error("Could not start the daemon");
return "notstarted";
}
$this->logger->notice("Daemon started", array('pid' => $pid));
return "started";
}
protected function stopDaemon()
{
if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) {
echo "Not started\n";
$this->logger->warning("Can't stop daemon as not started");
$pid = $this->getRunningPid();
if (!$pid) {
$this->logger->notice("Cannot stop the daemon as it is not started");
return "notstarted";
}
$cmd = "kill $(cat %s/daemon/daemon.pid)";
$command = sprintf($cmd, PHPCI_DIR);
exec($command);
$this->logger->info("Daemon stopped");
unlink(PHPCI_DIR.'/daemon/daemon.pid');
}
$this->logger->info("Trying to terminate the daemon", array('pid' => $pid));
$this->processControl->kill($pid);
protected function statusDaemon()
{
if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) {
echo "Not running\n";
return "notrunning";
for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; $i++) {
sleep(1);
}
$pid = trim(file_get_contents(PHPCI_DIR.'/daemon/daemon.pid'));
$pidcheck = sprintf("/proc/%s", $pid);
if (is_dir($pidcheck)) {
echo "Running\n";
if ($pid) {
$this->logger->warning("The daemon is resiting, trying to kill it", array('pid' => $pid));
$this->processControl->kill($pid, true);
for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; $i++) {
sleep(1);
}
}
if (!$pid) {
$this->logger->notice("Daemon stopped");
return "stopped";
}
$this->logger->error("Could not stop the daemon");
}
protected function statusDaemon(OutputInterface $output)
{
$pid = $this->getRunningPid();
if ($pid) {
$output->writeln(sprintf('The daemon is running, PID: %d', $pid));
return "running";
}
unlink(PHPCI_DIR.'/daemon/daemon.pid');
echo "Not running\n";
$output->writeln('The daemon is not running');
return "notrunning";
}
/** Check if there is a running daemon
*
* @return int|null
*/
protected function getRunningPid()
{
if (!file_exists($this->pidFilePath)) {
return;
}
$pid = intval(trim(file_get_contents($this->pidFilePath)));
if($this->processControl->isRunning($pid, true)) {
return $pid;
}
// Not found, remove the stale PID file
unlink($this->pidFilePath);
}
}

View file

@ -323,7 +323,9 @@ class InstallCommand extends Command
{
$output->write(Lang::get('setting_up_db'));
shell_exec(PHPCI_DIR . 'vendor/bin/phinx migrate -c "' . PHPCI_DIR . 'phinx.php"');
$phinxBinary = escapeshellarg(PHPCI_DIR . 'vendor/bin/phinx');
$phinxScript = escapeshellarg(PHPCI_DIR . 'phinx.php');
shell_exec($phinxBinary . ' migrate -c ' . $phinxScript);
$output->writeln('<info>'.Lang::get('ok').'</info>');
}

View file

@ -75,7 +75,8 @@ class RebuildCommand extends Command
$store = Factory::getStore('Build');
$service = new BuildService($store);
$lastBuild = array_shift($store->getLatestBuilds(null, 1));
$builds = $store->getLatestBuilds(null, 1);
$lastBuild = array_shift($builds);
$service->createDuplicateBuild($lastBuild);
$runner->run(new ArgvInput(array()), $output);

View file

@ -166,7 +166,7 @@ class RunCommand extends Command
$build->setStatus(Build::STATUS_FAILED);
$build->setFinished(new \DateTime());
$store->save($build);
$this->removeBuildDirectory($build);
$build->removeBuildDirectory();
continue;
}
@ -175,19 +175,4 @@ class RunCommand extends Command
return $rtn;
}
protected function removeBuildDirectory($build)
{
$buildPath = PHPCI_DIR . 'PHPCI/build/' . $build->getId() . '/';
if (is_dir($buildPath)) {
$cmd = 'rm -Rf "%s"';
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
shell_exec($cmd);
}
}
}

View file

@ -13,6 +13,7 @@ use b8;
use b8\Exception\HttpException\NotFoundException;
use b8\Http\Response\JsonResponse;
use PHPCI\BuildFactory;
use PHPCI\Helper\AnsiConverter;
use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use PHPCI\Model\Project;
@ -198,11 +199,7 @@ class BuildController extends \PHPCI\Controller
*/
protected function cleanLog($log)
{
$log = str_replace('[0;32m', '<span style="color: green">', $log);
$log = str_replace('[0;31m', '<span style="color: red">', $log);
$log = str_replace('[0m', '</span>', $log);
return $log;
return AnsiConverter::convert($log);
}
/**

View file

@ -15,6 +15,7 @@ use b8\Store;
use PHPCI\BuildFactory;
use PHPCI\Model\Project;
use PHPCI\Model\Build;
use PHPCI\Service\BuildStatusService;
/**
* Build Status Controller - Allows external access to build status information / images.
@ -24,10 +25,9 @@ use PHPCI\Model\Build;
*/
class BuildStatusController extends \PHPCI\Controller
{
/**
* @var \PHPCI\Store\ProjectStore
*/
/* @var \PHPCI\Store\ProjectStore */
protected $projectStore;
/* @var \PHPCI\Store\BuildStore */
protected $buildStore;
/**
@ -70,11 +70,70 @@ class BuildStatusController extends \PHPCI\Controller
return $status;
}
/**
* Displays projects information in ccmenu format
*
* @param $projectId
* @return bool
* @throws \Exception
* @throws b8\Exception\HttpException
*/
public function ccxml($projectId)
{
/* @var Project $project */
$project = $this->projectStore->getById($projectId);
$xml = new \SimpleXMLElement('<Projects/>');
if (!$project instanceof Project || !$project->getAllowPublicStatus()) {
return $this->renderXml($xml);
}
try {
$branchList = $this->buildStore->getBuildBranches($projectId);
if (!$branchList) {
$branchList = array($project->getBranch());
}
foreach ($branchList as $branch) {
$buildStatusService = new BuildStatusService($branch, $project, $project->getLatestBuild($branch));
if ($attributes = $buildStatusService->toArray()) {
$projectXml = $xml->addChild('Project');
foreach ($attributes as $attributeKey => $attributeValue) {
$projectXml->addAttribute($attributeKey, $attributeValue);
}
}
}
} catch (\Exception $e) {
$xml = new \SimpleXMLElement('<projects/>');
}
return $this->renderXml($xml);
}
/**
* @param \SimpleXMLElement $xml
* @return bool
*/
protected function renderXml(\SimpleXMLElement $xml = null)
{
$this->response->setHeader('Content-Type', 'text/xml');
$this->response->setContent($xml->asXML());
$this->response->flush();
echo $xml->asXML();
return true;
}
/**
* Returns the appropriate build status image in SVG format for a given project.
*/
public function image($projectId)
{
$style = $this->getParam('style', 'plastic');
$label = $this->getParam('label', 'build');
$status = $this->getStatus($projectId);
if (is_null($status)) {
@ -84,7 +143,13 @@ class BuildStatusController extends \PHPCI\Controller
}
$color = ($status == 'passing') ? 'green' : 'red';
$image = file_get_contents('http://img.shields.io/badge/build-' . $status . '-' . $color . '.svg');
$image = file_get_contents(sprintf(
'http://img.shields.io/badge/%s-%s-%s.svg?style=%s',
$label,
$status,
$color,
$style
));
$this->response->disableLayout();
$this->response->setHeader('Content-Type', 'image/svg+xml');

View file

@ -98,12 +98,22 @@ class HomeController extends \PHPCI\Controller
protected function getSummaryHtml($projects)
{
$summaryBuilds = array();
$successes = array();
$failures = array();
$successes = array();
$failures = array();
$counts = array();
foreach ($projects['items'] as $project) {
$summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId());
$count = $this->buildStore->getWhere(
array('project_id' => $project->getId()),
1,
0,
array(),
array('id' => 'DESC')
);
$counts[$project->getId()] = $count['count'];
$success = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_SUCCESS);
$failure = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_FAILED);
@ -112,10 +122,11 @@ class HomeController extends \PHPCI\Controller
}
$summaryView = new b8\View('SummaryTable');
$summaryView->projects = $projects['items'];
$summaryView->builds = $summaryBuilds;
$summaryView->projects = $projects['items'];
$summaryView->builds = $summaryBuilds;
$summaryView->successful = $successes;
$summaryView->failed = $failures;
$summaryView->failed = $failures;
$summaryView->counts = $counts;
return $summaryView->render();
}

View file

@ -86,7 +86,7 @@ class ProjectController extends PHPCI\Controller
$this->view->builds = $builds[0];
$this->view->total = $builds[1];
$this->view->project = $project;
$this->view->branch = urldecode($branch);
$this->view->branch = urldecode($branch);
$this->view->branches = $this->projectStore->getKnownBranches($projectId);
$this->view->page = $page;
$this->view->pages = $pages;

View file

@ -43,15 +43,23 @@ class SessionController extends \PHPCI\Controller
$isLoginFailure = false;
if ($this->request->getMethod() == 'POST') {
$user = $this->userStore->getByEmail($this->getParam('email'));
if ($user && password_verify($this->getParam('password', ''), $user->getHash())) {
$_SESSION['phpci_user_id'] = $user->getId();
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', $this->getLoginRedirect());
return $response;
} else {
$token = $this->getParam('token');
if ($token === null || $token !== $_SESSION['login_token']) {
$isLoginFailure = true;
} else {
unset($_SESSION['login_token']);
$user = $this->userStore->getByLoginOrEmail($this->getParam('email'));
if ($user && password_verify($this->getParam('password', ''), $user->getHash())) {
session_regenerate_id(true);
$_SESSION['phpci_user_id'] = $user->getId();
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', $this->getLoginRedirect());
return $response;
} else {
$isLoginFailure = true;
}
}
}
@ -60,7 +68,7 @@ class SessionController extends \PHPCI\Controller
$form->setAction(PHPCI_URL.'session/login');
$email = new b8\Form\Element\Email('email');
$email->setLabel(Lang::get('email_address'));
$email->setLabel(Lang::get('login'));
$email->setRequired(true);
$email->setContainerClass('form-group');
$email->setClass('form-control');
@ -78,9 +86,15 @@ class SessionController extends \PHPCI\Controller
$pwd->setClass('btn-success');
$form->addField($pwd);
$tokenValue = $this->generateToken();
$_SESSION['login_token'] = $tokenValue;
$token = new b8\Form\Element\Hidden('token');
$token->setValue($tokenValue);
$form->addField($token);
$this->view->form = $form->render();
$this->view->failed = $isLoginFailure;
return $this->view->render();
}
@ -180,4 +194,20 @@ class SessionController extends \PHPCI\Controller
return $rtn;
}
/** Generate a random token.
*
* @return string
*/
protected function generateToken()
{
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes(16));
}
return sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF))
. sprintf("%04x", mt_rand(0, 0xFFFF));
}
}

View file

@ -329,7 +329,7 @@ class SettingsController extends Controller
$field->setContainerClass('form-group');
$form->addField($field);
$field = new Form\Element\Text('smtp_password');
$field = new Form\Element\Password('smtp_password');
$field->setRequired(false);
$field->setLabel(Lang::get('smtp_password'));
$field->setClass('form-control');

View file

@ -2,7 +2,7 @@
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @copyright Copyright 2014-2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
@ -11,31 +11,37 @@ namespace PHPCI\Controller;
use b8;
use b8\Store;
use Exception;
use PHPCI\BuildFactory;
use PHPCI\Model\Project;
use PHPCI\Service\BuildService;
use PHPCI\Store\BuildStore;
use PHPCI\Store\ProjectStore;
/**
* Webhook Controller - Processes webhook pings from BitBucket, Github, Gitlab, etc.
*
* @author Dan Cryer <dan@block8.co.uk>
* @author Sami Tikka <stikka@iki.fi>
* @author Alex Russell <alex@clevercherry.com>
* @author Guillaume Perréal <adirelle@gmail.com>
* @package PHPCI
* @subpackage Web
*/
class WebhookController extends \PHPCI\Controller
class WebhookController extends \b8\Controller
{
/**
* @var \PHPCI\Store\BuildStore
* @var BuildStore
*/
protected $buildStore;
/**
* @var \PHPCI\Store\ProjectStore
* @var ProjectStore
*/
protected $projectStore;
/**
* @var \PHPCI\Service\BuildService
* @var BuildService
*/
protected $buildService;
@ -49,283 +55,311 @@ class WebhookController extends \PHPCI\Controller
$this->buildService = new BuildService($this->buildStore);
}
/** Handle the action, Ensuring to return a JsonResponse.
*
* @param string $action
* @param mixed $actionParams
*
* @return \b8\Http\Response
*/
public function handleAction($action, $actionParams)
{
$response = new b8\Http\Response\JsonResponse();
try {
$data = parent::handleAction($action, $actionParams);
if (isset($data['responseCode'])) {
$response->setResponseCode($data['responseCode']);
unset($data['responseCode']);
}
$response->setContent($data);
} catch (Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
}
return $response;
}
/**
* Called by Bitbucket POST service.
*/
public function bitbucket($project)
public function bitbucket($projectId)
{
$response = new b8\Http\Response\JsonResponse();
$response->setContent(array('status' => 'ok'));
$project = $this->fetchProject($projectId, 'bitbucket');
$payload = json_decode($this->getParam('payload'), true);
$results = array();
$status = 'failed';
foreach ($payload['commits'] as $commit) {
try {
$email = $commit['raw_author'];
$email = substr($email, 0, strpos($email, '>'));
$email = substr($email, strpos($email, '<') + 1);
$this->createBuild($project, $commit['raw_node'], $commit['branch'], $email, $commit['message']);
} catch (\Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
break;
$results[$commit['raw_node']] = $this->createBuild(
$project,
$commit['raw_node'],
$commit['branch'],
$email,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['raw_node']] = array('status' => 'failed', 'error' => $ex->getMessage());
}
}
return $response;
return array('status' => $status, 'commits' => $results);
}
/**
* Called by POSTing to /webhook/git/<project_id>?branch=<branch>&commit=<commit>
*
* @param string $project
* @param string $projectId
*/
public function git($project)
public function git($projectId)
{
$response = new b8\Http\Response\JsonResponse();
$response->setContent(array('status' => 'ok'));
$branch = $this->getParam('branch');
$project = $this->fetchProject($projectId, array('local', 'remote'));
$branch = $this->getParam('branch', $project->getBranch());
$commit = $this->getParam('commit');
$commitMessage = $this->getParam('message');
$committer = $this->getParam('committer');
try {
if (empty($branch)) {
$branch = 'master';
}
if (empty($commit)) {
$commit = null;
}
if (empty($commitMessage)) {
$commitMessage = null;
}
if (empty($committer)) {
$committer = null;
}
$this->createBuild($project, $commit, $branch, $committer, $commitMessage);
} catch (\Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
}
return $response;
return $this->createBuild($project, $commit, $branch, $committer, $commitMessage);
}
/**
* Called by Github Webhooks:
*/
public function github($project)
public function github($projectId)
{
$response = new b8\Http\Response\JsonResponse();
$response->setContent(array('status' => 'ok'));
$project = $this->fetchProject($projectId, 'github');
switch ($_SERVER['CONTENT_TYPE']) {
case 'application/json':
$payload = json_decode(file_get_contents('php://input'), true);
break;
case 'application/x-www-form-urlencoded':
$payload = json_decode($this->getParam('payload'), true);
break;
default:
$response->setResponseCode(401);
$response->setContent(array('status' => 'failed', 'error' => 'Content type not supported.'));
return $response;
return array('status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401);
}
// Handle Pull Request web hooks:
if (array_key_exists('pull_request', $payload)) {
return $this->githubPullRequest($project, $payload, $response);
return $this->githubPullRequest($project, $payload);
}
// Handle Push web hooks:
if (array_key_exists('commits', $payload)) {
return $this->githubCommitRequest($project, $payload, $response);
return $this->githubCommitRequest($project, $payload);
}
return $response;
return array('status' => 'ignored', 'message' => 'Unusable payload.');
}
/**
* Handle the payload when Github sends a commit webhook.
* @param $project
*
* @param Project $project
* @param array $payload
* @param b8\Http\Response\JsonResponse $response
*
* @return b8\Http\Response\JsonResponse
*/
protected function githubCommitRequest($project, array $payload, b8\Http\Response\JsonResponse $response)
protected function githubCommitRequest(Project $project, array $payload)
{
// Github sends a payload when you close a pull request with a
// non-existant commit. We don't want this.
if (array_key_exists('after', $payload) && $payload['after'] === '0000000000000000000000000000000000000000') {
return $response;
return array('status' => 'ignored');
}
try {
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
foreach ($payload['commits'] as $commit) {
if (!$commit['distinct']) {
continue;
}
$results = array();
$status = 'failed';
foreach ($payload['commits'] as $commit) {
if (!$commit['distinct']) {
$results[$commit['id']] = array('status' => 'ignored');
continue;
}
try {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['committer']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
$results[$commit['id']] = $this->createBuild(
$project,
$commit['id'],
$branch,
$committer,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage());
}
} elseif (substr($payload['ref'], 0, 10) == 'refs/tags/') {
// If we don't, but we're dealing with a tag, add that instead:
$branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']);
$committer = $payload['pusher']['email'];
$message = $payload['head_commit']['message'];
$this->createBuild($project, $payload['after'], $branch, $committer, $message);
}
} catch (\Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
return array('status' => $status, 'commits' => $results);
}
return $response;
if (substr($payload['ref'], 0, 10) == 'refs/tags/') {
// If we don't, but we're dealing with a tag, add that instead:
$branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']);
$committer = $payload['pusher']['email'];
$message = $payload['head_commit']['message'];
return $this->createBuild($project, $payload['after'], $branch, $committer, $message);
}
return array('status' => 'ignored', 'message' => 'Unusable payload.');
}
/**
* Handle the payload when Github sends a Pull Request webhook.
* @param $projectId
*
* @param Project $project
* @param array $payload
*/
protected function githubPullRequest($projectId, array $payload, b8\Http\Response\JsonResponse $response)
protected function githubPullRequest(Project $project, array $payload)
{
// We only want to know about open pull requests:
if (!in_array($payload['action'], array('opened', 'synchronize', 'reopened'))) {
return $response;
return array('status' => 'ok');
}
try {
$headers = array();
$token = \b8\Config::getInstance()->get('phpci.github.token');
$headers = array();
$token = \b8\Config::getInstance()->get('phpci.github.token');
if (!empty($token)) {
$headers[] = 'Authorization: token ' . $token;
if (!empty($token)) {
$headers[] = 'Authorization: token ' . $token;
}
$url = $payload['pull_request']['commits_url'];
$http = new \b8\HttpClient();
$http->setHeaders($headers);
$response = $http->get($url);
// Check we got a success response:
if (!$response['success']) {
throw new Exception('Could not get commits, failed API request.');
}
$results = array();
$status = 'failed';
foreach ($response['body'] as $commit) {
// Skip all but the current HEAD commit ID:
$id = $commit['sha'];
if ($id != $payload['pull_request']['head']['sha']) {
$results[$id] = array('status' => 'ignored', 'message' => 'not branch head');
continue;
}
$url = $payload['pull_request']['commits_url'];
$http = new \b8\HttpClient();
$http->setHeaders($headers);
$response = $http->get($url);
// Check we got a success response:
if (!$response['success']) {
$message = 'Could not get commits, failed API request.';
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $message));
return $response;
}
foreach ($response['body'] as $commit) {
// Skip all but the current HEAD commit ID:
if ($commit['sha'] != $payload['pull_request']['head']['sha']) {
continue;
}
try {
$branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']);
$committer = $commit['commit']['author']['email'];
$message = $commit['commit']['message'];
$remoteUrlKey = $payload['pull_request']['head']['repo']['private'] ? 'ssh_url' : 'clone_url';
$extra = array(
'build_type' => 'pull_request',
'pull_request_id' => $payload['pull_request']['id'],
'pull_request_number' => $payload['number'],
'remote_branch' => $payload['pull_request']['head']['ref'],
'remote_url' => $payload['pull_request']['head']['repo']['clone_url'],
'remote_url' => $payload['pull_request']['head']['repo'][$remoteUrlKey],
);
$this->createBuild($projectId, $commit['sha'], $branch, $committer, $message, $extra);
$results[$id] = $this->createBuild($project, $id, $branch, $committer, $message, $extra);
$status = 'ok';
} catch (Exception $ex) {
$results[$id] = array('status' => 'failed', 'error' => $ex->getMessage());
}
} catch (\Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
}
return $response;
return array('status' => $status, 'commits' => $results);
}
/**
* Called by Gitlab Webhooks:
*/
public function gitlab($project)
public function gitlab($projectId)
{
$response = new b8\Http\Response\JsonResponse();
$response->setContent(array('status' => 'ok'));
$project = $this->fetchProject($projectId, 'gitlab');
$payloadString = file_get_contents("php://input");
$payload = json_decode($payloadString, true);
try {
// build on merge request events
if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') {
$attributes = $payload['object_attributes'];
if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') {
$branch = $attributes['source_branch'];
$commit = $attributes['last_commit'];
$committer = $commit['author']['email'];
// build on merge request events
if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') {
$attributes = $payload['object_attributes'];
if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') {
$branch = $attributes['source_branch'];
$commit = $attributes['last_commit'];
$committer = $commit['author']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
}
return $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
}
// build on push events
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
foreach ($payload['commits'] as $commit) {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['author']['email'];
$this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
}
}
} catch (\Exception $ex) {
$response->setResponseCode(500);
$response->setContent(array('status' => 'failed', 'error' => $ex->getMessage()));
}
return $response;
// build on push events
if (isset($payload['commits']) && is_array($payload['commits'])) {
// If we have a list of commits, then add them all as builds to be tested:
$results = array();
$status = 'failed';
foreach ($payload['commits'] as $commit) {
try {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['author']['email'];
$results[$commit['id']] = $this->createBuild(
$project,
$commit['id'],
$branch,
$committer,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage());
}
}
return array('status' => $status, 'commits' => $results);
}
return array('status' => 'ignored', 'message' => 'Unusable payload.');
}
/**
* Wrapper for creating a new build.
* @param $projectId
* @param $commitId
* @param $branch
* @param $committer
* @param $commitMessage
* @param null $extra
* @return bool
* @throws \Exception
*
* @param Project $project
* @param string $commitId
* @param string $branch
* @param string $committer
* @param string $commitMessage
* @param array $extra
*
* @return array
*
* @throws Exception
*/
protected function createBuild($projectId, $commitId, $branch, $committer, $commitMessage, $extra = null)
{
protected function createBuild(
Project $project,
$commitId,
$branch,
$committer,
$commitMessage,
array $extra = null
) {
// Check if a build already exists for this commit ID:
$builds = $this->buildStore->getByProjectAndCommit($projectId, $commitId);
$builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId);
if ($builds['count']) {
return true;
}
$project = $this->projectStore->getById($projectId);
if (empty($project)) {
throw new \Exception('Project does not exist:' . $projectId);
return array(
'status' => 'ignored',
'message' => sprintf('Duplicate of build #%d', $builds['items'][0]->getId())
);
}
// If not, create a new build job for it:
@ -335,6 +369,34 @@ class WebhookController extends \PHPCI\Controller
// Send a status postback if the build type provides one:
$build->sendStatusPostback();
return true;
return array('status' => 'ok', 'buildID' => $build->getID());
}
/**
* Fetch a project and check its type.
*
* @param int $projectId
* @param array|string $expectedType
*
* @return Project
*
* @throws Exception If the project does not exist or is not of the expected type.
*/
protected function fetchProject($projectId, $expectedType)
{
$project = $this->projectStore->getById($projectId);
if (empty($projectId)) {
throw new Exception('Project does not exist: ' . $projectId);
}
if (is_array($expectedType)
? !in_array($project->getType(), $expectedType)
: $project->getType() !== $expectedType
) {
throw new Exception('Wrong project type: ' . $project->getType());
}
return $project;
}
}

68
PHPCI/ErrorHandler.php Normal file
View file

@ -0,0 +1,68 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI;
/**
* Error Handler
*
* @package PHPCI\Logging
*/
class ErrorHandler
{
/**
* @var array
*/
protected $levels = array(
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
);
/**
* Registers an instance of the error handler to throw ErrorException.
*/
public static function register()
{
$handler = new static();
set_error_handler(array($handler, 'handleError'));
}
/**
* @param integer $level
* @param string $message
* @param string $file
* @param integer $line
*
* @throws \ErrorException
*
* @internal
*/
public function handleError($level, $message, $file, $line)
{
if (error_reporting() & $level === 0) {
return;
}
$exceptionLevel = isset($this->levels[$level]) ? $this->levels[$level] : $level;
throw new \ErrorException(
sprintf('%s: %s in %s line %d', $exceptionLevel, $message, $file, $line),
0,
$level,
$file,
$line
);
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Helper;
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
/**
* Converts ANSI output to HTML.
*
* @package PHPCI\Helper
*/
final class AnsiConverter
{
static private $converter = null;
/**
* Initialize the singletion.
*
* @return AnsiToHtmlConverter
*/
private static function getInstance()
{
if (self::$converter === null) {
self::$converter = new AnsiToHtmlConverter(null, false);
}
return self::$converter;
}
/**
* Convert a text containing ANSI colr sequences into HTML code.
*
* @param string $text The text to convert
*
* @return string The HTML code.
*/
public static function convert($text)
{
return self::getInstance()->convert($text);
}
/**
* Do not instanciate this class.
*/
private function __construct()
{
}
}

View file

@ -9,9 +9,9 @@
namespace PHPCI\Helper;
use \PHPCI\Logging\BuildLogger;
use Exception;
use PHPCI\Logging\BuildLogger;
use Psr\Log\LogLevel;
use PHPCI\Helper\Lang;
/**
* Handles running system commands with variables.
@ -20,7 +20,7 @@ use PHPCI\Helper\Lang;
abstract class BaseCommandExecutor implements CommandExecutor
{
/**
* @var \PHPCI\Logging\BuildLogger
* @var BuildLogger
*/
protected $logger;
@ -144,13 +144,12 @@ abstract class BaseCommandExecutor implements CommandExecutor
/**
* Find a binary required by a plugin.
* @param string $binary
* @param null $buildPath
* @param bool $quiet
* @return null|string
*/
public function findBinary($binary, $buildPath = null)
public function findBinary($binary, $quiet = false)
{
$binaryPath = null;
$composerBin = $this->getComposerBinDir(realpath($buildPath));
$composerBin = $this->getComposerBinDir(realpath($this->buildPath));
if (is_string($binary)) {
$binary = array($binary);
@ -161,30 +160,30 @@ abstract class BaseCommandExecutor implements CommandExecutor
if (is_dir($composerBin) && is_file($composerBin.'/'.$bin)) {
$this->logger->log(Lang::get('found_in_path', $composerBin, $bin), LogLevel::DEBUG);
$binaryPath = $composerBin . '/' . $bin;
break;
return $composerBin . '/' . $bin;
}
if (is_file($this->rootDir . $bin)) {
$this->logger->log(Lang::get('found_in_path', 'root', $bin), LogLevel::DEBUG);
$binaryPath = $this->rootDir . $bin;
break;
return $this->rootDir . $bin;
}
if (is_file($this->rootDir . 'vendor/bin/' . $bin)) {
$this->logger->log(Lang::get('found_in_path', 'vendor/bin', $bin), LogLevel::DEBUG);
$binaryPath = $this->rootDir . 'vendor/bin/' . $bin;
break;
return $this->rootDir . 'vendor/bin/' . $bin;
}
$findCmdResult = $this->findGlobalBinary($bin);
if (is_file($findCmdResult)) {
$this->logger->log(Lang::get('found_in_path', '', $bin), LogLevel::DEBUG);
$binaryPath = $findCmdResult;
break;
return $findCmdResult;
}
}
return $binaryPath;
if ($quiet) {
return;
}
throw new Exception(Lang::get('could_not_find', implode('/', $binary)));
}
/**

View file

@ -26,10 +26,13 @@ interface CommandExecutor
/**
* Find a binary required by a plugin.
* @param string $binary
* @param string $buildPath the current build path
* @param bool $quiet Returns null instead of throwing an execption.
*
* @return null|string
*
* @throws \Exception when no binary has been found and $quiet is false.
*/
public function findBinary($binary, $buildPath = null);
public function findBinary($binary, $quiet = false);
/**
* Set the buildPath property.

View file

@ -156,7 +156,7 @@ class Lang
return null;
}
require_once($langFile);
require($langFile);
if (is_null($strings) || !is_array($strings) || !count($strings)) {
return null;

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Adgangskode-nulstilling for %s',
'reset_invalid' => 'Ugyldig anmodning om adgangskode-nulstilling.',
'email_address' => 'Email-addresse',
'login' => 'Login / Email Address',
'password' => 'Adgangskode',
'log_in' => 'Log ind',
@ -126,6 +127,7 @@ i din foretrukne hosting-platform.',
'all_branches' => 'Alle branches',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Date',
'project' => 'Projekt',
'commit' => 'Commit',
'branch' => 'Branch',
@ -193,14 +195,20 @@ Services</a> sektionen under dit Bitbucket-repository.',
'end' => 'Slut',
'from' => 'Fra',
'to' => 'Til',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat',
'ok' => 'OK',
'took_n_seconds' => 'Tog %d sekunder',
'build_created' => 'Build Oprettet',
'build_started' => 'Build Startet',
'build_finished' => 'Build Afsluttet',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Navn',
@ -296,15 +304,15 @@ du kører composer update.',
Kontrollér venligst nedenstående fejl før du fortsætter.',
'must_be_valid_email' => 'Skal være en gyldig email-adresse.',
'must_be_valid_url' => 'Skal være en gyldig URL.',
'enter_name' => 'Administrator-navn:',
'enter_email' => 'Administrators email-adresse:',
'enter_password' => 'Administrator-adgangskode:',
'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"):',
'enter_name' => 'Administrator-navn: ',
'enter_email' => 'Administrators email-adresse: ',
'enter_password' => 'Administrator-adgangskode: ',
'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"): ',
'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]:',
'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]:',
'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]:',
'enter_db_pass' => 'Indtast dit MySQL-password:',
'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]: ',
'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]: ',
'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]: ',
'enter_db_pass' => 'Indtast dit MySQL-password: ',
'could_not_connect' => 'PHPCI kunne ikke forbinde til MySQL med de angivning oplysninger. Forsøg igen.',
'setting_up_db' => 'Indlæser database...',
'user_created' => 'Brugerkonto oprettet!',
@ -331,6 +339,12 @@ Kontrollér venligst nedenstående fejl før du fortsætter.',
'create_admin_user' => 'Tilføj en administrator',
'incorrect_format' => 'Forkert format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Kør alle PHPCI builds i køen.',
'finding_builds' => 'Finder builds der skal køres',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Passwort zurücksetzen für %s',
'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes',
'email_address' => 'Emailadresse',
'login' => 'Login / Email Address',
'password' => 'Passwort',
'log_in' => 'Einloggen',
@ -101,7 +102,8 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'remote' => 'Externe URL',
'local' => 'Lokaler Pfad',
'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => 'Wo wird Ihr Projekt gehostet?',
'choose_github' => 'Wählen Sie ein GitHub Repository:',
@ -127,6 +129,7 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'all_branches' => 'Alle Branches',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Date',
'project' => 'Projekt',
'commit' => 'Commit',
'branch' => 'Branch',
@ -181,7 +184,15 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit',
'technical_debt' => 'Technische Schulden',
'behat' => 'Behat',
'codeception_feature' => 'Feature',
'codeception_suite' => 'Suite',
'codeception_time' => 'Zeit',
'codeception_synopsis' => '<strong>%1$d</strong> Tests in <strong>%2$f</strong> Sekunden ausgeführt.
<strong>%3$d</strong> Fehler.',
'file' => 'Datei',
'line' => 'Zeile',
'class' => 'Klasse',
@ -191,14 +202,20 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'end' => 'Ende',
'from' => 'Von',
'to' => 'Bis',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat',
'ok' => 'OK',
'took_n_seconds' => 'Benötigte %d Sekunden',
'build_created' => 'Build erstellt',
'build_started' => 'Build gestartet',
'build_finished' => 'Build abgeschlossen',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Name',
@ -294,9 +311,9 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
Bitte überprüfen Sie die Fehler, bevor Sie fortfahren,',
'must_be_valid_email' => 'Muss eine gültige Emailadresse sein.',
'must_be_valid_url' => 'Muss eine valide URL sein.',
'enter_name' => 'Name des Administrators:',
'enter_email' => 'Emailadresse des Administrators:',
'enter_password' => 'Passwort des Administrators:',
'enter_name' => 'Name des Administrators: ',
'enter_email' => 'Emailadresse des Administrators: ',
'enter_password' => 'Passwort des Administrators: ',
'enter_phpci_url' => 'Ihre PHPCI-URL (z.B. "http://phpci.local"): ',
'enter_db_host' => 'Bitte geben Sie Ihren MySQL-Host ein [localhost]: ',
@ -329,6 +346,12 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
'create_admin_user' => 'Administratorenbenutzer erstellen',
'incorrect_format' => 'Falsches Format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Führe alle ausstehenden PHPCI Builds aus.',
'finding_builds' => 'Suche verarbeitbare Builds',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Επαναφορά Κωδικού για %s',
'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.',
'email_address' => 'Διεύθυνση email',
'login' => 'Login / Email Address',
'password' => 'Κωδικός πρόσβασης',
'log_in' => 'Είσοδος',
@ -127,6 +128,7 @@ PHPCI',
'all_branches' => 'Όλες οι διακλαδώσεις',
'builds' => 'Κατασκευές',
'id' => 'Αριθμός αναγνώρισης',
'date' => 'Date',
'project' => 'Έργο',
'commit' => 'Συνεισφορά',
'branch' => 'Διακλάδωση',
@ -170,6 +172,7 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'codeception_errors' => 'Λάθη Codeception',
'phpmd_warnings' => 'Προειδοποιήσεις PHPMD',
'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ',
'codeception_errors' => 'Λάθη Codeception',
'phpcs_errors' => 'Λάθη PHPCS',
'phplint_errors' => 'Λάθη Lint',
'phpunit_errors' => 'Λάθη PHPUnit ',
@ -193,14 +196,20 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'end' => 'Τέλος',
'from' => 'Από',
'to' => 'Προς',
'suite' => 'Σουίτα',
'test' => 'Τέστ',
'result' => 'Αποτέλεσμα',
'ok' => 'ΟΚ',
'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα',
'build_created' => 'Η κατασκευή δημιουργήθηκε',
'build_started' => 'Η κατασκευή άρχισε',
'build_finished' => 'Η κατασκευή ολοκληρώθηκε',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Όνομα',
@ -297,15 +306,15 @@ Services</a> του Bitbucket αποθετηρίου σας.',
Παρακαλούμε διαβάστε τα παραπάνω λάθη πριν συνεχίσετε.',
'must_be_valid_email' => 'Πρέπει να είναι μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.',
'must_be_valid_url' => 'Πρέπει να είναι μια έγκυρη διεύθυνση URL.',
'enter_name' => 'Όνομα διαχειριστή:',
'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή:',
'enter_password' => 'Κωδικός πρόσβασης διαχειριστή:',
'enter_name' => 'Όνομα διαχειριστή: ',
'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή: ',
'enter_password' => 'Κωδικός πρόσβασης διαχειριστή: ',
'enter_phpci_url' => 'Ο URL σύνδεσμος σας για το PHPCI ("http://phpci.local" για παράδειγμα): ',
'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]:',
'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]: ',
'enter_db_name' => 'Παρακαλώ εισάγετε το όνομα της MySQL βάσης δεδομένων σας [phpci]: ',
'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]:',
'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας:',
'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]: ',
'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας: ',
'could_not_connect' => 'Το PHPCI δεν μπόρεσε να συνδεθεί με την MySQL με τα στοχεία που δώσατε. Παρακαλώ δοκιμάστε ξανά.',
'setting_up_db' => 'Γίνεται ρύθμιση της βάσης δεδομένων σας ...',
'user_created' => 'Λογαριασμός χρήστη δημιουργήθηκε!',
@ -332,6 +341,12 @@ Services</a> του Bitbucket αποθετηρίου σας.',
'create_admin_user' => 'Δημιουργήστε ένα χρήστη διαχειριστή',
'incorrect_format' => 'Λανθασμένη μορφοποίηση',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Εκτελέστε όλες τις εκκρεμείς PHPCI κατασκευές.',
'finding_builds' => 'Αναζήτηση κατασκευών για επεξεργασία',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI Password Reset for %s',
'reset_invalid' => 'Invalid password reset request.',
'email_address' => 'Email Address',
'login' => 'Login / Email Address',
'password' => 'Password',
'log_in' => 'Log in',
@ -128,6 +129,7 @@ PHPCI',
'all_branches' => 'All Branches',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Date',
'project' => 'Project',
'commit' => 'Commit',
'branch' => 'Branch',
@ -188,6 +190,12 @@ PHPCI',
'technical_debt' => 'Technical Debt',
'behat' => 'Behat',
'codeception_feature' => 'Feature',
'codeception_suite' => 'Suite',
'codeception_time' => 'Time',
'codeception_synopsis' => '<strong>%1$d</strong> tests carried out in <strong>%2$f</strong> seconds.
<strong>%3$d</strong> failures.',
'file' => 'File',
'line' => 'Line',
'class' => 'Class',
@ -197,14 +205,20 @@ PHPCI',
'end' => 'End',
'from' => 'From',
'to' => 'To',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Result',
'ok' => 'OK',
'took_n_seconds' => 'Took %d seconds',
'build_created' => 'Build Created',
'build_started' => 'Build Started',
'build_finished' => 'Build Finished',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Name',
@ -281,6 +295,17 @@ PHPCI',
'search_packagist_for_more' => 'Search Packagist for more packages',
'search' => 'Search &raquo;',
// Summary plugin
'build-summary' => 'Summary',
'stage' => 'Stage',
'duration' => 'Duration',
'plugin' => 'Plugin',
'stage_setup' => 'Setup',
'stage_test' => 'Test',
'stage_complete' => 'Complete',
'stage_success' => 'Success',
'stage_failure' => 'Failure',
// Installer
'installation_url' => 'PHPCI Installation URL',
'db_host' => 'Database Host',
@ -301,9 +326,9 @@ PHPCI',
Please review the errors above before continuing.',
'must_be_valid_email' => 'Must be a valid email address.',
'must_be_valid_url' => 'Must be a valid URL.',
'enter_name' => 'Admin Name:',
'enter_email' => 'Admin Email:',
'enter_password' => 'Admin Password:',
'enter_name' => 'Admin Name: ',
'enter_email' => 'Admin Email: ',
'enter_password' => 'Admin Password: ',
'enter_phpci_url' => 'Your PHPCI URL ("http://phpci.local" for example): ',
'enter_db_host' => 'Please enter your MySQL host [localhost]: ',
@ -336,6 +361,12 @@ PHPCI',
'create_admin_user' => 'Create an admin user',
'incorrect_format' => 'Incorrect format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Run all pending PHPCI builds.',
'finding_builds' => 'Finding builds to process',

386
PHPCI/Languages/lang.es.php Normal file
View file

@ -0,0 +1,386 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
$strings = array(
'language_name' => 'Español',
'language' => 'Lenguaje',
// Log in:
'log_in_to_phpci' => 'Ingresar a PHPCI',
'login_error' => 'Email o contraseña incorrectos',
'forgotten_password_link' => '¿Olvidaste tu contraseña?',
'reset_emailed' => 'Te hemos enviado un email para reiniciar tu contraseña.',
'reset_header' => '<strong>¡No te preocupes!</strong><br>Solo tienes que ingresar tu dirección de email
y te enviaremos por email un enlace para reiniciar tu contraseña.',
'reset_email_address' => 'Ingresa tu dirección de email:',
'reset_send_email' => 'Enviar enlace',
'reset_enter_password' => 'Ingresa una nueva contraseña',
'reset_new_password' => 'Nueva contraseña:',
'reset_change_password' => 'Cambiar contraseña',
'reset_no_user_exists' => 'No existe ningún usuario con ese email, por favor intenta nuevamente.',
'reset_email_body' => 'Hola %s,
Has recibido este correo porque , o alguien más, ha solicitado reiniciar la contraseña de PHPCI
Si fuiste , por favor haz click en el siguiente enlace para reiniciar tu contraseña: %ssession/reset-password/%d/%s
De lo contrario, por favor ignora este correo y ninguna acción será realizada.
Gracias,
PHPCI',
'reset_email_title' => 'Reiniciar contraseña de PHPCI para %s',
'reset_invalid' => 'Pedido inválido.',
'email_address' => 'Dirección de email',
'password' => 'Contraseña',
'log_in' => 'Ingresar',
// Top Nav
'toggle_navigation' => 'Activar navegación',
'n_builds_pending' => '%d builds pendientes',
'n_builds_running' => '%d builds ejecutándose',
'edit_profile' => 'Editar Perfil',
'sign_out' => 'Cerrar Sesión',
'branch_x' => 'Rama: %s',
'created_x' => 'Creada el: %s',
'started_x' => 'Comenzó: %s',
// Sidebar
'hello_name' => 'Hola, %s',
'dashboard' => 'Escritorio',
'admin_options' => 'Opciones de Admin.',
'add_project' => 'Agregar Proyecto',
'settings' => 'Configuración',
'manage_users' => 'Administrar Usuarios',
'plugins' => 'Plugins',
'view' => 'Vista',
'build_now' => 'Ejecutar Build',
'edit_project' => 'Editar Proyecto',
'delete_project' => 'Eliminar Proyecto',
// Project Summary:
'no_builds_yet' => '¡No existen builds aún!',
'x_of_x_failed' => '%d de los últimos %d builds fallaron.',
'x_of_x_failed_short' => '%d / %d fallaron.',
'last_successful_build' => ' El último build exitoso fue %s.',
'never_built_successfully' => ' Este proyecto nunca tuvo un build exitoso.',
'all_builds_passed' => 'Todos los últimos %d builds pasaron.',
'all_builds_passed_short' => '%d / %d pasaron.',
'last_failed_build' => ' El último build en fallar fue %s.',
'never_failed_build' => ' Este proyecto no tiene ningún build fallido.',
'view_project' => 'Ver Proyecto',
// Timeline:
'latest_builds' => 'Últimos builds',
'pending' => 'Pediente',
'running' => 'Ejecutando',
'success' => 'Éxito',
'successful' => 'Exitoso',
'failed' => 'Falló',
'manual_build' => 'Build Manual',
// Add/Edit Project:
'new_project' => 'Nuevo Proyecto',
'project_x_not_found' => 'El Proyecto con ID %d no existe.',
'project_details' => 'Detalles del Proyecto',
'public_key_help' => 'Para facilitarte, hemos generado un par de llaves SSH para que uses en este proyecto.
Para usarlo, sólo agrega la siguiente llave pública a la sección de "deploy keys"
de tu plataforma de hosting de versionado de código.',
'select_repository_type' => 'Selecciona tipo de repositorio...',
'github' => 'GitHub',
'bitbucket' => 'Bitbucket',
'gitlab' => 'GitLab',
'remote' => 'URL Remota',
'local' => 'Path local',
'hg' => 'Mercurial',
'svn' => 'Subversion',
'where_hosted' => '¿Dónde está alojado tu proyecto?',
'choose_github' => 'Selecciona un repositorio de GitHub:',
'repo_name' => 'Nombre del repositorio / URL (Remoto) o Ruta (Local)',
'project_title' => 'Titulo del proyecto',
'project_private_key' => 'Clave privada a usar para acceder al repositorio
(dejar en blanco para remotos locales o anónimos)',
'build_config' => 'Configuración PHPCI para builds del proyecto
(en caso que no puedas agregar el archivo phpci.yml al repositorio)',
'default_branch' => 'Nombre de la rama por defecto',
'allow_public_status' => '¿Activar página pública con el estado del proyecto?',
'archived' => 'Archivado',
'save_project' => 'Guardar Proyecto',
'error_mercurial' => 'La URL del repositorio de Mercurial debe comenzar con http:// or https://',
'error_remote' => 'La URL del repositorio debe comenzar con git://, http:// or https://',
'error_gitlab' => 'El nombre del repositorio de GitLab debe tener el formato "user@domain.tld:owner/repo.git"',
'error_github' => 'El nombre del repositorio debe tener el formato "owner/repo"',
'error_bitbucket' => 'El nombre del repo debe tener el formato "owner/repo"',
'error_path' => 'La ruta especificada no existe.',
// View Project:
'all_branches' => 'Todas las ramas',
'builds' => 'Builds',
'id' => 'ID',
'project' => 'Proyecto',
'commit' => 'Commit',
'branch' => 'Rama',
'status' => 'Estado',
'prev_link' => '&laquo; Anterior',
'next_link' => 'Siguiente &raquo;',
'public_key' => 'Llave pública',
'delete_build' => 'Eliminar Build',
'webhooks' => 'Webhooks',
'webhooks_help_github' => 'Para compilar automáticamente este proyecto cada vez que se realiza un commit, agreagar la siguiente URL
como un nuevo "webhook" en la sección <a href="https://github.com/%s/settings/hooks">Webhooks
and Services</a> de tu repositorio en GitHub.',
'webhooks_help_gitlab' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL
como una "WebHook URL" en la sección "web hooks" de tu repositorio en GitLab.',
'webhooks_help_bitbucket' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL
como un servicio "POST" en la sección
<a href="https://bitbucket.org/%s/admin/services">
Services</a> de tu repositorio en Bitbucket.',
// View Build
'build_x_not_found' => 'El build con ID %d no existe.',
'build_n' => 'Build %d',
'rebuild_now' => 'Rebuild Ahora',
'committed_by_x' => 'Commit hecho por %s',
'commit_id_x' => 'Commit: %s',
'chart_display' => 'Este gráfico será mostrado una vez que el build se haya completado.',
'build' => 'Build',
'lines' => 'Líneas',
'comment_lines' => 'Líneas de comentario',
'noncomment_lines' => 'Líneas no comentario',
'logical_lines' => 'Líneas lógicas',
'lines_of_code' => 'Líneas de código',
'build_log' => 'Log',
'quality_trend' => 'Tendencia de calidad',
'codeception_errors' => 'Errores de Codeception',
'phpmd_warnings' => 'PHPMD Warnings',
'phpcs_warnings' => 'PHPCS Warnings',
'phpcs_errors' => 'PHPCS Errors',
'phplint_errors' => 'Lint Errors',
'phpunit_errors' => 'PHPUnit Errors',
'phpdoccheck_warnings' => 'Docblocks faltantes',
'issues' => 'Incidencias',
'codeception' => 'Codeception',
'phpcpd' => 'PHP Copy/Paste Detector',
'phpcs' => 'PHP Code Sniffer',
'phpdoccheck' => 'Missing Docblocks',
'phpmd' => 'PHP Mess Detector',
'phpspec' => 'PHP Spec',
'phpunit' => 'PHP Unit',
'technical_debt' => 'Deuda Técnica',
'behat' => 'Behat',
'file' => 'Archivo',
'line' => 'Línea',
'class' => 'Clase',
'method' => 'Método',
'message' => 'Mensaje',
'start' => 'Inicio',
'end' => 'Fin',
'from' => 'De',
'to' => 'Para',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultado',
'ok' => 'OK',
'took_n_seconds' => 'Tomó %d segundos',
'build_created' => 'Build Creado',
'build_started' => 'Build Comenzado',
'build_finished' => 'Build Terminado',
// Users
'name' => 'Nombre',
'password_change' => 'Contraseña (dejar en blanco si no quiere cambiarla)',
'save' => 'Guardar &raquo;',
'update_your_details' => 'Actualizar los detalles',
'your_details_updated' => 'Tus detalles han sido actualizados.',
'add_user' => 'Agregar Usuario',
'is_admin' => '¿Es Admin?',
'yes' => 'Si',
'no' => 'No',
'edit' => 'Editar',
'edit_user' => 'Editar Usuario',
'delete_user' => 'Delete Usuario',
'user_n_not_found' => 'Usuario con ID %d no existe.',
'is_user_admin' => '¿Es un usuario administrador?',
'save_user' => 'Guardar Usuario',
// Settings:
'settings_saved' => 'Tu configuración ha sido guardada.',
'settings_check_perms' => 'Tu configuración no fue guardada, verificar los permisos del archivo config.yml.',
'settings_cannot_write' => 'PHPCI no puede escribir en el archivo config.yml file, la configuración no será guardada correctamente
hasta no corregir esto.',
'settings_github_linked' => 'Tu cuenta GitHub ha sido conectada.',
'settings_github_not_linked' => 'No se pudo conectar a tu cuenta GitHub.',
'build_settings' => 'Configuración del Build ',
'github_application' => 'Aplicación GitHub',
'github_sign_in' => 'Antes de comenzar a utilizar GitHub, tienes que <a href="%s">ingresar</a> y permitir
el acceso a tu cuenta a PHPCI.',
'github_phpci_linked' => 'PHPCI ha sido conectado a tu cuenta GitHub.',
'github_where_to_find' => 'Donde encontrar estos...',
'github_where_help' => 'Si eres priopietario de la aplicaión que quieres usar, puedes encontrar esta información en
el área de configuración de <a href="https://github.com/settings/applications">aplicaciones</a>.',
'email_settings' => 'Configuraciones de Email',
'email_settings_help' => 'Para que PHPCI pueda enviar email con el status de los builds,
debes configurar las siguientes propiedades SMTP.',
'application_id' => 'ID de aplicación',
'application_secret' => 'Application Secret',
'smtp_server' => 'Servidor SMTP',
'smtp_port' => 'Puerto SMTP',
'smtp_username' => 'Usuario SMTP',
'smtp_password' => 'Contraseña SMTP',
'from_email_address' => 'Dirección de email DE',
'default_notification_address' => 'Dirección de correo de notificación por defecto',
'use_smtp_encryption' => 'Usar encriptación SMTP?',
'none' => 'None',
'ssl' => 'SSL',
'tls' => 'TLS',
'failed_after' => 'Considerar el build como fallido luego de ',
'5_mins' => '5 Minutos',
'15_mins' => '15 Minutos',
'30_mins' => '30 Minutos',
'1_hour' => '1 Hora',
'3_hours' => '3 Horas',
// Plugins
'cannot_update_composer' => 'PHPCI no puede actualizar composer.json porque no tiene permisos de escritura.',
'x_has_been_removed' => '%s ha sido elimiando.',
'x_has_been_added' => '%s ha sido agregado a composer.json y será instalado la próxima vez que ejecutes composer update.',
'enabled_plugins' => 'Activar Plugins',
'provided_by_package' => 'Provisto por Paquete',
'installed_packages' => 'Paquetes Instalados',
'suggested_packages' => 'Paquetes Sugeridos',
'title' => 'Título',
'description' => 'Descripción',
'version' => 'Versión',
'install' => 'Instalar &raquo;',
'remove' => 'Eliminar &raquo;',
'search_packagist_for_more' => 'Buscar más paquetes en Packagist',
'search' => 'Buscar &raquo;',
// Installer
'installation_url' => 'URL de la instalación PHPCI',
'db_host' => 'Host',
'db_name' => 'Nombre de la base de datos',
'db_user' => 'Usuario de la base de datos',
'db_pass' => 'Clave de la base de datos',
'admin_name' => 'Nombre del Admin',
'admin_pass' => 'Clave del Admin',
'admin_email' => 'Email de Admin',
'config_path' => 'Ruta al archivo config',
'install_phpci' => 'Instalar PHPCI',
'welcome_to_phpci' => 'Bienvenido a PHPCI',
'please_answer' => 'Por favor, responde las siguientes preguntas:',
'phpci_php_req' => 'PHPCI requiere al menos PHP 5.3.8 para funcionar.',
'extension_required' => 'Extensión requerida: %s',
'function_required' => 'PHPCI debe poder invocar la función %s(). Está deshabilitada en php.ini?',
'requirements_not_met' => 'PHPCI no pudo ser instalado, ya que no se cumplen todos los requerimientos.
Por favor, corrige los errores antes de continuar.',
'must_be_valid_email' => 'Debe ser una dirección de correos válida.',
'must_be_valid_url' => 'Debe ser una URL válida.',
'enter_name' => 'Nombre del Admin:',
'enter_email' => 'Email del Admin:',
'enter_password' => 'Contraseña de Admin:',
'enter_phpci_url' => 'La URL de PHPCI ("Por ejemplo: http://phpci.local"): ',
'enter_db_host' => 'Por favor, ingresa el servidor MySQL [localhost]: ',
'enter_db_name' => 'Por favor, ingresa el nombre de la base de datos MySQL [phpci]: ',
'enter_db_user' => 'Por favor, ingresa el usuario MySQL [phpci]: ',
'enter_db_pass' => 'Por favor, ingresa la contraseña MySQL: ',
'could_not_connect' => 'PHPCI no pudo conectarse a MySQL con los datos dados. Por favor, intenta nuevamente.',
'setting_up_db' => 'Configurando base de datos... ',
'user_created' => '¡Cuenta de usuario creada!',
'failed_to_create' => 'PHPCI no pudo crear la cuenta de admin.',
'config_exists' => 'El archivo config de PHPCI ya existe y no es vacío.',
'update_instead' => 'Si está intentando actualizar PHPCI, por favor, utiliza phpci:update.',
// Update
'update_phpci' => 'Actuliza la base de datos para reflejar los modelos actualizados.',
'updating_phpci' => 'Actualizando base de datos PHPCI: ',
'not_installed' => 'PHPCI no está instalado.',
'install_instead' => 'Por favor, instala PHPCI via phpci:install.',
// Poll Command
'poll_github' => 'Chequear en GitHub si se necesita comenzar un Build.',
'no_token' => 'No se encontró ningún token GitHub',
'finding_projects' => 'Buscando proyectos para chequear',
'found_n_projects' => 'Se encontraron %d proyectos',
'last_commit_is' => 'El último commit en GitHub para %s es %s',
'adding_new_build' => 'Último commit es diferente a la base de datos, agregando nuevo build.',
'finished_processing_builds' => 'Fin de procesamiento de builds.',
// Create Admin
'create_admin_user' => 'Crear un usuario Admin',
'incorrect_format' => 'Formato incorrecto',
// Run Command
'run_all_pending' => 'Ejecutar todos los builds PHPCI pendientes.',
'finding_builds' => 'Buscando builds a procesar',
'found_n_builds' => 'Se encontraron %d builds',
'skipping_build' => 'Saltando Build %d - Build del proyecto ya en ejecución.',
'marked_as_failed' => 'Build %d falló debido a timeout.',
// Builder
'missing_phpci_yml' => 'Este proyecto no contiene el archivo phpci.yml o está vacío.',
'build_success' => 'BUILD EXITOSO',
'build_failed' => 'BUILD FALLIDO',
'removing_build' => 'Eliminando Build.',
'exception' => 'Excepción: ',
'could_not_create_working' => 'Imposible crear copia de trabajo.',
'working_copy_created' => 'Copia de trabajo creada: %s',
'looking_for_binary' => 'Buscando binario: %s',
'found_in_path' => 'Encontrado en %s: %s',
'running_plugin' => 'EJECUTANDO PLUGIN: %s',
'plugin_success' => 'PLUGIN: EXITO',
'plugin_failed' => 'PLUGIN: FALLÓ',
'plugin_missing' => 'No existe el plugin: %s',
'tap_version' => 'TapParser únicamente soporta la verisón 13 de TAP',
'tap_error' => 'Cadena de caracteres TAP inválida, el número de tests no coincide con la cuenta de tests declarada.',
// Build Plugins:
'no_tests_performed' => 'No se ejecutaron tests.',
'could_not_find' => 'No se encontró %s',
'no_campfire_settings' => 'No se especificaron parámetros de conexión para el plugin Campfire',
'failed_to_wipe' => 'Imposible eliminar directorio existente %s antes de copiarlo',
'passing_build' => 'Build Exitoso',
'failing_build' => 'Build Fallido',
'log_output' => 'Log de Salida: ',
'n_emails_sent' => '%d emails enviados.',
'n_emails_failed' => '%d emails no pudieron ser enviados.',
'unable_to_set_env' => 'Imposible setear variable de entorno',
'tag_created' => 'Tag creado por PHPCI: %s',
'x_built_at_x' => 'Build de %PROJECT_TITLE% en %BUILD_URI%',
'hipchat_settings' => 'Por favor, definir room y authToken para el plugin hipchat_notify',
'irc_settings' => 'Debes configurar un servidor, room y apodo.',
'invalid_command' => 'Comando inválido',
'import_file_key' => 'Sentencia de importación debe contener una llave \'file\'',
'cannot_open_import' => 'Imposible abrir archivo de importación SQL: %s',
'unable_to_execute' => 'Imposible ejecutar archivo SQL',
'phar_internal_error' => 'Error interno en plugin Phar',
'build_file_missing' => 'El archivo de build especificado no existe.',
'property_file_missing' => 'El archivo de propiedades especificado no existe.',
'could_not_process_report' => 'Imposible procesar el reporte generado por la herramienta.',
'shell_not_enabled' => 'El plugin shell no está habilitado. Por favor, habilitalo desde config.yml.'
);

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Réinitialisation du mot de passe PHPCI pour %s',
'reset_invalid' => 'Requête de réinitialisation de mot de passe invalide.',
'email_address' => 'Adresse email',
'login' => 'Login / Email Address',
'password' => 'Mot de passe',
'log_in' => 'Connexion',
@ -127,6 +128,7 @@ PHPCI',
'all_branches' => 'Toutes les branches',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Date',
'project' => 'Projet',
'commit' => 'Commit',
'branch' => 'Branche',
@ -194,14 +196,20 @@ PHPCI',
'end' => 'Fin',
'from' => 'À partir de',
'to' => 'jusque',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultat',
'ok' => 'OK',
'took_n_seconds' => 'Exécuté en %d secondes',
'build_created' => 'Build créé',
'build_started' => 'Build démarré',
'build_finished' => 'Build terminé',
'test_message' => 'Message',
'test_no_message' => 'Pas de message',
'test_success' => 'Réussi(s): %d',
'test_fail' => 'Echec(s): %d',
'test_skipped' => 'Passé(s): %d',
'test_error' => 'Erreurs: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Nom',
@ -264,7 +272,7 @@ PHPCI',
// Plugins
'cannot_update_composer' => 'PHPCI ne peut pas mettre à jour le fichier composer.json pour vous, il n\'est pas modifiable.',
'x_has_been_removed' => '%s a été supprimé.',
'x_has_been_added' => '%s a été ajouté au fichier composer.json poru vous et il sera installé la prochaine fois
'x_has_been_added' => '%s a été ajouté au fichier composer.json pour vous et il sera installé la prochaine fois
que vous lancerez "composer update".',
'enabled_plugins' => 'Plugins activés',
'provided_by_package' => 'Fournis par le paquet',
@ -278,6 +286,17 @@ PHPCI',
'search_packagist_for_more' => 'Rechercher sur Packagist pour trouver plus de paquets',
'search' => 'Rechercher &raquo;',
// Summary plugin
'build-summary' => 'Résumé',
'stage' => 'Étape',
'duration' => 'Durée',
'plugin' => 'Plugin',
'stage_setup' => 'Préparation',
'stage_test' => 'Test',
'stage_complete' => 'Terminé',
'stage_success' => 'Succes',
'stage_failure' => 'Échec',
// Installer
'installation_url' => 'URL d\'installation de PHPCI',
'db_host' => 'Hôte de la BDD',
@ -298,9 +317,9 @@ PHPCI',
Merci de corriger les erreurs ci-dessus avant de continuer.',
'must_be_valid_email' => 'Doit être une adresse email valide.',
'must_be_valid_url' => 'Doit être une URL valide.',
'enter_name' => 'Nom de l\'admin :',
'enter_email' => 'Email de l\'admin :',
'enter_password' => 'Mot de passe de l\'admin :',
'enter_name' => 'Nom de l\'admin: ',
'enter_email' => 'Email de l\'admin: ',
'enter_password' => 'Mot de passe de l\'admin: ',
'enter_phpci_url' => 'Votre URL vers PHPCI (par exemple "http://phpci.local"): ',
'enter_db_host' => 'Merci d\'entrer le nom d\'hôte MySQL [localhost]: ',
@ -333,6 +352,12 @@ PHPCI',
'create_admin_user' => 'Créer un utilisateur admin',
'incorrect_format' => 'Format incorrect',
// Create Build Command
'create_build_project' => 'Créer un build projet',
'project_id_argument' => 'ID du projet',
'commit_id_option' => 'ID du commit',
'branch_name_option' => 'Branche',
// Run Command
'run_all_pending' => 'Démarrage de tout les builds PHPCI en attente.',
'finding_builds' => 'Découverte des builds à traiter',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Ripristino della password di PHPCI per %s',
'reset_invalid' => 'Richeista di ripristino password non valida.',
'email_address' => 'Indirizzo Email',
'login' => 'Login / Email Address',
'password' => 'Password',
'log_in' => 'Accedi',
@ -127,6 +128,7 @@ PHPCI',
'all_branches' => 'Tutti i Branche',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Data',
'project' => 'Progetto',
'commit' => 'Commit',
'branch' => 'Branch',
@ -196,14 +198,20 @@ PHPCI',
'end' => 'Finisci',
'from' => 'Da',
'to' => 'A',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Risultati',
'ok' => 'OK',
'took_n_seconds' => 'Sono stati impiegati %d seconds',
'build_created' => 'Build Creata',
'build_started' => 'Build Avviata',
'build_finished' => 'Build Terminata',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Nome',
@ -300,9 +308,9 @@ PHPCI',
Per favore controlla gli errori riportati prima di proseguire.',
'must_be_valid_email' => 'Deve essere un indirizzo email valido.',
'must_be_valid_url' => 'Deve essere un URL valido.',
'enter_name' => 'Nome dell\'amministratore:',
'enter_email' => 'Email dell\'amministratore:',
'enter_password' => 'Password dell\'amministratore:',
'enter_name' => 'Nome dell\'amministratore: ',
'enter_email' => 'Email dell\'amministratore: ',
'enter_password' => 'Password dell\'amministratore: ',
'enter_phpci_url' => 'L\'URL di PHPCI ("http://phpci.locale" ad esempio): ',
'enter_db_host' => 'Per favore inserisci l\'host MySQL [localhost]: ',
@ -335,6 +343,12 @@ PHPCI',
'create_admin_user' => 'Crea un nuovo utente amministrarore',
'incorrect_format' => 'Formato errato',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Esegui tutte le build in attesa su PHPCI.',
'finding_builds' => 'Ricerca delel build da processare',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'PHPCI wachtwoord reset voor %s',
'reset_invalid' => 'Ongeldig wachtwoord reset verzoek',
'email_address' => 'E-mailadres',
'login' => 'Login / Email Address',
'password' => 'Wachtwoord',
'log_in' => 'Log in',
@ -127,6 +128,7 @@ van je gekozen source code hosting platform',
'all_branches' => 'Alle brances',
'builds' => 'Builds',
'id' => 'ID',
'date' => 'Datum',
'project' => 'Project',
'commit' => 'Commit',
'branch' => 'Branch',
@ -194,14 +196,20 @@ Services</a> sectie van je Bitbucket repository toegevoegd worden.',
'end' => 'Einde',
'from' => 'Van',
'to' => 'Tot',
'suite' => 'Suite',
'test' => 'Test',
'result' => 'Resultaat',
'ok' => 'OK',
'took_n_seconds' => 'Duurde %d seconden',
'build_created' => 'Build aangemaakt',
'build_started' => 'Build gestart',
'build_finished' => 'Build beëindigd',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Naam',
@ -298,15 +306,15 @@ keer je composer update uitvoert.',
Gelieve de fouten na te kijken vooraleer verder te gaan.',
'must_be_valid_email' => 'Moet een geldig e-mailadres zijn.',
'must_be_valid_url' => 'Moet een geldige URL zijn.',
'enter_name' => 'Administrator naam:',
'enter_email' => 'Administrator e-mailadres:',
'enter_password' => 'Administrator wachtwoord:',
'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local")',
'enter_name' => 'Administrator naam: ',
'enter_email' => 'Administrator e-mailadres: ',
'enter_password' => 'Administrator wachtwoord: ',
'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local"): ',
'enter_db_host' => 'Vul je MySQL host in [localhost]:',
'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]:',
'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]:',
'enter_db_pass' => 'Vul je MySQL watchtwoord in:',
'enter_db_host' => 'Vul je MySQL host in [localhost]: ',
'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]: ',
'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]: ',
'enter_db_pass' => 'Vul je MySQL watchtwoord in: ',
'could_not_connect' => 'PHPCI kon met deze gegevens geen verbinding maken met MySQL. Gelieve opnieuw te proberen.',
'setting_up_db' => 'Database wordt aangemaakt...',
'user_created' => 'Gebruikersprofiel aangemaakt!',
@ -333,6 +341,12 @@ Gelieve de fouten na te kijken vooraleer verder te gaan.',
'create_admin_user' => 'Administrator-gebruiker aanmaken',
'incorrect_format' => 'Incorrect formaat',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Voer alle wachtende PHPCI builds uit.',
'finding_builds' => 'Zoekt builds om te verwerken',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Reset Hasła PHPCI dla %s',
'reset_invalid' => 'Prośba o zmianę hasła jest nieważna.',
'email_address' => 'Adres email',
'login' => 'Login / Email Address',
'password' => 'Hasło',
'log_in' => 'Zaloguj się',
@ -128,6 +129,7 @@ od wybranego kodu źródłowego platformy hostingowej.',
'all_branches' => 'Wszystkie Gałęzie',
'builds' => 'Budowania',
'id' => 'ID',
'date' => 'Date',
'project' => 'Projekt',
'commit' => 'Commit',
'branch' => 'Gałąź',
@ -197,14 +199,20 @@ Services</a> repozytoria Bitbucket.',
'end' => 'Koniec',
'from' => 'Od',
'to' => 'Do',
'suite' => 'Zestaw ',
'test' => 'Test',
'result' => 'Wynik',
'ok' => 'OK',
'took_n_seconds' => 'Zajęło %d sekund',
'build_created' => 'Budowanie Stworzone',
'build_started' => 'Budowanie Rozpoczęte',
'build_finished' => 'Budowanie Zakończone',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Nazwa',
@ -299,15 +307,15 @@ wywołaniu polecenia composer update.',
Przejrzyj powyższą listę błędów przed kontynuowaniem.',
'must_be_valid_email' => 'Poprawny adres email jest wymagany.',
'must_be_valid_url' => 'Poprawny URL jest wymagany.',
'enter_name' => 'Imię Admina:',
'enter_email' => 'Email Admina:',
'enter_password' => 'Hasło Admina:',
'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"):',
'enter_name' => 'Imię Admina: ',
'enter_email' => 'Email Admina: ',
'enter_password' => 'Hasło Admina: ',
'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"): ',
'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]:',
'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]:',
'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]:',
'enter_db_pass' => 'Wpisz hasło MySQL:',
'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]: ',
'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]: ',
'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]: ',
'enter_db_pass' => 'Wpisz hasło MySQL: ',
'could_not_connect' => 'Z podanymi ustawieniami PHPCI nie udało się połączyć z MySQL. Spróbuj ponownie.',
'setting_up_db' => 'Ustawianie Twojej bazy danych...',
'user_created' => 'Utworzono konto użytkownika!',
@ -334,6 +342,12 @@ Przejrzyj powyższą listę błędów przed kontynuowaniem.',
'create_admin_user' => 'Utwórz admina',
'incorrect_format' => 'Niepoprawny format',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Uruchom wszystkie oczekujące budowy w PHPCI',
'finding_builds' => 'Szukam budów do przetwarzania.',

View file

@ -38,6 +38,7 @@ PHPCI',
'reset_email_title' => 'Сброс пароля PHPCI для %s',
'reset_invalid' => 'Некорректный запрос на сброс пароля.',
'email_address' => 'Email',
'login' => 'Логин / Email',
'password' => 'Пароль',
'log_in' => 'Войти',
@ -112,7 +113,7 @@ PHPCI',
(если вы не добавили файл phpci.yml в репозиторий вашего проекта)',
'default_branch' => 'Ветка по умолчанию',
'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта',
'archived' => 'Archived',
'archived' => 'Запакован',
'save_project' => 'Сохранить проект',
'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://',
@ -126,6 +127,7 @@ PHPCI',
'all_branches' => 'Все ветки',
'builds' => 'Сборки',
'id' => 'ID',
'date' => 'Дата',
'project' => 'Проект',
'commit' => 'Коммит',
'branch' => 'Ветка',
@ -188,18 +190,24 @@ PHPCI',
'class' => 'Класс',
'method' => 'Метод',
'message' => 'Сообщение',
'start' => 'Запуск',
'start' => 'Начало',
'end' => 'Конец',
'from' => 'От',
'to' => 'До',
'suite' => 'Комплект',
'test' => 'Тест',
'from' => 'Из',
'to' => 'В',
'result' => 'Результат',
'ok' => 'OK',
'took_n_seconds' => 'Заняло секунд: %d',
'build_created' => 'Сборка создана',
'build_started' => 'Сборка запущена',
'build_finished' => 'Сборка окончена',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Имя',
@ -294,9 +302,9 @@ PHPCI',
Пожалуйста, просмотрите возникшие ошибки перед тем, как продолжить.',
'must_be_valid_email' => 'Должен быть корректным email-адресом.',
'must_be_valid_url' => 'Должен быть корректным URL-адресом.',
'enter_name' => 'Имя администратора:',
'enter_email' => 'Email администратора:',
'enter_password' => 'Пароль администратора:',
'enter_name' => 'Имя администратора: ',
'enter_email' => 'Email администратора: ',
'enter_password' => 'Пароль администратора: ',
'enter_phpci_url' => 'URL-адрес вашего PHPCI (например: "http://phpci.local"): ',
'enter_db_host' => 'Пожалуйста, введите хост MySQL [localhost]: ',
@ -329,6 +337,12 @@ PHPCI',
'create_admin_user' => 'Добавить аккаунт администратора',
'incorrect_format' => 'Неверный формат',
// Create Build Command
'create_build_project' => 'Создать сборку проекта',
'project_id_argument' => 'ID проекта',
'commit_id_option' => 'ID коммита для сборки',
'branch_name_option' => 'Ветка для сборки',
// Run Command
'run_all_pending' => 'Запустить все ожидающие PHPCI сборки.',
'finding_builds' => 'Поиск сборок для запуска',

View file

@ -39,6 +39,7 @@ PHPCI',
'reset_email_title' => 'Скидання пароль PHPCI для %s',
'reset_invalid' => 'Невірний запит скидання паролю.',
'email_address' => 'Email адреса',
'login' => 'Логин / Email адреса',
'password' => 'Пароль',
'log_in' => 'Увійти',
@ -126,6 +127,7 @@ PHPCI',
'all_branches' => 'Усі гілки',
'builds' => 'Збірки',
'id' => 'ID',
'date' => 'Дата',
'project' => 'Проект',
'commit' => 'Комміт',
'branch' => 'Гілка',
@ -194,14 +196,20 @@ PHPCI',
'end' => 'Кінець',
'from' => 'Від',
'to' => 'До',
'suite' => 'Комплект',
'test' => 'Тест',
'result' => 'Результат',
'ok' => 'OK',
'took_n_seconds' => 'Зайняло %d секунд',
'build_created' => 'Збірка створена',
'build_started' => 'Збірка розпочата',
'build_finished' => 'Збірка завершена',
'test_message' => 'Message',
'test_no_message' => 'No message',
'test_success' => 'Succesfull: %d',
'test_fail' => 'Failures: %d',
'test_skipped' => 'Skipped: %d',
'test_error' => 'Errors: %d',
'test_todo' => 'Todos: %d',
'test_total' => '%d test(s)',
// Users
'name' => 'Ім’я',
@ -298,15 +306,15 @@ PHPCI',
Будь ласка, продивіться наявні помилки перед тим, як продовжити.',
'must_be_valid_email' => 'Повинно бути коректною email адресою.',
'must_be_valid_url' => 'Повинно бути коректним URL.',
'enter_name' => 'Ім’я адміністратора:',
'enter_email' => 'Email адміністратора:',
'enter_password' => 'Пароль адміністратора:',
'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"):',
'enter_name' => 'Ім’я адміністратора: ',
'enter_email' => 'Email адміністратора: ',
'enter_password' => 'Пароль адміністратора: ',
'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"): ',
'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]:',
'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]:',
'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]:',
'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL:',
'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]: ',
'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]: ',
'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]: ',
'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL: ',
'could_not_connect' => 'PHPCI не може підключитися до MySQL із наданими параметрами. Будь ласка, спробуйте ще раз.',
'setting_up_db' => 'Налаштування вашої бази даних...',
'user_created' => 'Аккаунт користувача створено!',
@ -333,6 +341,12 @@ PHPCI',
'create_admin_user' => 'Створити аккаунт адміністратора',
'incorrect_format' => 'Невірний формат',
// Create Build Command
'create_build_project' => 'Create a build for a project',
'project_id_argument' => 'A project ID',
'commit_id_option' => 'Commit ID to build',
'branch_name_option' => 'Branch to build',
// Run Command
'run_all_pending' => 'Запустити всі PHPCI збірки, які очікують.',
'finding_builds' => 'Пошук збірок для обробки',

View file

@ -9,6 +9,7 @@
namespace PHPCI\Logging;
use Monolog\ErrorHandler;
use Monolog\Logger;
/**
@ -19,6 +20,7 @@ class LoggerConfig
{
const KEY_ALWAYS_LOADED = "_";
private $config;
private $cache = array();
/**
* The filepath is expected to return an array which will be
@ -56,9 +58,20 @@ class LoggerConfig
*/
public function getFor($name)
{
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$handlers = $this->getHandlers(self::KEY_ALWAYS_LOADED);
$handlers = array_merge($handlers, $this->getHandlers($name));
return new Logger($name, $handlers);
if ($name !== self::KEY_ALWAYS_LOADED) {
$handlers = array_merge($handlers, $this->getHandlers($name));
}
$logger = new Logger($name, $handlers);
ErrorHandler::register($logger);
$this->cache[$name] = $logger;
return $logger;
}
/**

View file

@ -0,0 +1,30 @@
<?php
use Phinx\Migration\AbstractMigration;
class UniqueEmailAndNameUserFields extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$user_table = $this->table('user');
$user_table
->addIndex('email', array('unique' => true))
->addIndex('name', array('unique' => true))
->save();
}
/**
* Migrate Down.
*/
public function down()
{
$user_table = $this->table('user');
$user_table
->removeIndex('email', array('unique' => true))
->removeIndex('name', array('unique' => true))
->save();
}
}

View file

@ -217,4 +217,31 @@ class Build extends BuildBase
{
return array($builder, $file, $line, $message);
}
/**
* Return the path to run this build into.
*
* @return string|null
*/
public function getBuildPath()
{
if (!$this->getId()) {
return null;
}
return PHPCI_BUILD_ROOT_DIR . $this->getId();
}
/**
* Removes the build directory.
*/
public function removeBuildDirectory()
{
$buildPath = $this->getBuildPath();
if (!$buildPath || !is_dir($buildPath)) {
return;
}
exec(sprintf(IS_WIN ? 'rmdir /S /Q "%s"' : 'rm -Rf "%s"', $buildPath));
}
}

View file

@ -125,7 +125,7 @@ class GithubBuild extends RemoteGitBuild
if ($this->getExtra('build_type') == 'pull_request') {
$matches = array();
preg_match('/\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches);
preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches);
$reference = $matches[1];
$branch = $this->getExtra('remote_branch');

View file

@ -47,7 +47,7 @@ class GitlabBuild extends RemoteGitBuild
'http://%s/%s/blob/%s/{FILE}#L{LINE}',
$this->getProject()->getAccessInformation("domain"),
$this->getProject()->getReference(),
$this->getBranch()
$this->getCommitId()
);
}

View file

@ -54,7 +54,7 @@ class RemoteGitBuild extends Build
*/
protected function cloneByHttp(Builder $builder, $cloneTo)
{
$cmd = 'git clone ';
$cmd = 'git clone --recursive ';
$depth = $builder->getConfig('clone_depth');
@ -84,7 +84,7 @@ class RemoteGitBuild extends Build
}
// Do the git clone:
$cmd = 'git clone ';
$cmd = 'git clone --recursive ';
$depth = $builder->getConfig('clone_depth');
@ -124,16 +124,16 @@ class RemoteGitBuild extends Build
$success = true;
$commit = $this->getCommitId();
$chdir = IS_WIN ? 'cd /d "%s"' : 'cd "%s"';
if (!empty($commit) && $commit != 'Manual') {
$cmd = 'cd "%s"';
$cmd = $chdir . ' && git checkout %s --quiet';
$success = $builder->executeCommand($cmd, $cloneTo, $commit);
}
if (IS_WIN) {
$cmd = 'cd /d "%s"';
}
$cmd .= ' && git checkout %s --quiet';
$success = $builder->executeCommand($cmd, $cloneTo, $this->getCommitId());
// Always update the commit hash with the actual HEAD hash
if ($builder->executeCommand($chdir . ' && git rev-parse HEAD', $cloneTo)) {
$this->setCommitId(trim($builder->getLastOutput()));
}
return $success;

View file

@ -98,7 +98,6 @@ class Behat implements \PHPCI\Plugin
$lines = explode(PHP_EOL, $parts[1]);
$errorCount = 0;
$storeFailures = false;
$data = array();

View file

@ -9,50 +9,72 @@
namespace PHPCI\Plugin;
use PHPCI;
use PHPCI\Builder;
use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use PHPCI\Plugin\Util\TapParser;
use PHPCI\Plugin\Util\TestResultParsers\Codeception as Parser;
use Psr\Log\LogLevel;
/**
* Codeception Plugin - Enables full acceptance, unit, and functional testing
*
* Codeception Plugin - Enables full acceptance, unit, and functional testing.
* @author Don Gilbert <don@dongilbert.net>
* @author Igor Timoshenko <contact@igortimoshenko.com>
* @author Adam Cooper <adam@networkpie.co.uk>
* @package PHPCI
* @subpackage Plugins
*/
class Codeception implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
class Codeception implements \PHPCI\Plugin, \PHPCI\ZeroConfigPlugin
{
/**
* @var string
*/
/** @var string */
protected $args = '';
/**
* @var Build
*/
/** @var Builder */
protected $phpci;
/** @var Build */
protected $build;
/**
* @var Builder
* @var string $ymlConfigFile The path of a yml config for Codeception
*/
protected $phpci;
protected $ymlConfigFile;
/**
* @var string|string[] The path (or array of paths) of an yml config for Codeception
* @var string $path The path to the codeception tests folder.
*/
protected $configFile;
protected $path;
/**
* @var string The path where the reports and logs are stored
* @param $stage
* @param Builder $builder
* @param Build $build
* @return bool
*/
protected $logPath = 'tests/_output';
public static function canExecute($stage, Builder $builder, Build $build)
{
return $stage == 'test' && !is_null(self::findConfigFile($builder->buildPath));
}
/**
* Try and find the codeception YML config file.
* @param $buildPath
* @return null|string
*/
public static function findConfigFile($buildPath)
{
if (file_exists($buildPath . 'codeception.yml')) {
return 'codeception.yml';
}
if (file_exists($buildPath . 'codeception.dist.yml')) {
return 'codeception.dist.yml';
}
return null;
}
/**
* Set up the plugin, configure options, etc.
*
* @param Builder $phpci
* @param Build $build
* @param array $options
@ -61,110 +83,83 @@ class Codeception implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
{
$this->phpci = $phpci;
$this->build = $build;
$this->path = 'tests/_output/';
if (isset($options['config'])) {
$this->configFile = $options['config'];
if (empty($options['config'])) {
$this->ymlConfigFile = self::findConfigFile($this->phpci->buildPath);
} else {
$this->ymlConfigFile = $options['config'];
}
if (isset($options['args'])) {
$this->args = (string) $options['args'];
}
if (isset($options['log_path'])) {
$this->logPath = $options['log_path'];
if (isset($options['path'])) {
$this->path = $options['path'];
}
}
/**
* {@inheritDoc}
* Runs Codeception tests, optionally using specified config file(s).
*/
public function execute()
{
$success = true;
if (empty($this->ymlConfigFile)) {
throw new \Exception("No configuration file found");
}
// Run any config files first. This can be either a single value or an array.
return $this->runConfigFile($this->ymlConfigFile);
}
/**
* Run tests from a Codeception config file.
* @param $configPath
* @return bool|mixed
* @throws \Exception
*/
protected function runConfigFile($configPath)
{
$this->phpci->logExecOutput(false);
// Run any config files first. This can be either a single value or an array
if ($this->configFile !== null) {
$success &= $this->runConfigFile($this->configFile);
$codecept = $this->phpci->findBinary('codecept');
if (!$codecept) {
$this->phpci->logFailure(Lang::get('could_not_find', 'codecept'));
return false;
}
$tapString = file_get_contents(
$this->phpci->buildPath . $this->logPath . DIRECTORY_SEPARATOR . 'report.tap.log'
$cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
if (IS_WIN) {
$cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args;
}
$configPath = $this->phpci->buildPath . $configPath;
$success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath);
$this->phpci->log(
'Codeception XML path: '. $this->phpci->buildPath . $this->path . 'report.xml',
Loglevel::DEBUG
);
$xml = file_get_contents($this->phpci->buildPath . $this->path . 'report.xml', false);
$parser = new Parser($this->phpci, $xml);
$output = $parser->parse();
$meta = array(
'tests' => $parser->getTotalTests(),
'timetaken' => $parser->getTotalTimeTaken(),
'failures' => $parser->getTotalFailures()
);
try {
$tapParser = new TapParser($tapString);
$output = $tapParser->parse();
} catch (\Exception $ex) {
$this->phpci->logFailure($tapString);
throw $ex;
}
$failures = $tapParser->getTotalFailures();
$this->build->storeMeta('codeception-errors', $failures);
$this->build->storeMeta('codeception-meta', $meta);
$this->build->storeMeta('codeception-data', $output);
$this->build->storeMeta('codeception-errors', $parser->getTotalFailures());
$this->phpci->logExecOutput(true);
return $success;
}
/**
* {@inheritDoc}
*/
public static function canExecute($stage, Builder $builder, Build $build)
{
return $stage === 'test';
}
/**
* Run tests from a Codeception config file
*
* @param string $configPath
* @return bool|mixed
*/
protected function runConfigFile($configPath)
{
if (is_array($configPath)) {
return $this->recurseArg($configPath, array($this, 'runConfigFile'));
} else {
$codecept = $this->phpci->findBinary('codecept');
if (!$codecept) {
$this->phpci->logFailure(Lang::get('could_not_find', 'codecept'));
return false;
}
$cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --tap ' . $this->args;
if (IS_WIN) {
$cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --tap ' . $this->args;
}
$configPath = $this->phpci->buildPath . $configPath;
$success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath);
return $success;
}
}
/**
* @param array $array
* @param \Callback $callable
* @return bool|mixed
*/
protected function recurseArg(array $array, $callable)
{
$success = true;
foreach ($array as $subItem) {
$success &= call_user_func($callable, $subItem);
}
return $success;
}
}

View file

@ -27,6 +27,7 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
protected $preferDist;
protected $phpci;
protected $build;
protected $nodev;
/**
* Check if this plugin can be executed.
@ -60,6 +61,7 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$this->directory = $path;
$this->action = 'install';
$this->preferDist = false;
$this->nodev = false;
if (array_key_exists('directory', $options)) {
$this->directory = $path . '/' . $options['directory'];
@ -72,6 +74,10 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
if (array_key_exists('prefer_dist', $options)) {
$this->preferDist = (bool)$options['prefer_dist'];
}
if (array_key_exists('no_dev', $options)) {
$this->nodev = (bool)$options['no_dev'];
}
}
/**
@ -81,11 +87,6 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
{
$composerLocation = $this->phpci->findBinary(array('composer', 'composer.phar'));
if (!$composerLocation) {
$this->phpci->logFailure(Lang::get('could_not_find', 'composer'));
return false;
}
$cmd = '';
if (IS_WIN) {
@ -102,6 +103,11 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$cmd .= '--prefer-source';
}
if ($this->nodev) {
$this->phpci->log('Using --no-dev flag');
$cmd .= ' --no-dev';
}
$cmd .= ' --working-dir="%s" %s';
return $this->phpci->executeCommand($cmd, $this->directory, $this->action);

View file

@ -9,11 +9,13 @@
namespace PHPCI\Plugin;
use Exception;
use b8\View;
use PHPCI\Builder;
use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use PHPCI\Helper\Email as EmailHelper;
use Psr\Log\LogLevel;
/**
* Email Plugin - Provides simple email capability to PHPCI.
@ -70,12 +72,25 @@ class Email implements \PHPCI\Plugin
$buildStatus = $this->build->isSuccessful() ? "Passing Build" : "Failing Build";
$projectName = $this->build->getProject()->getTitle();
$mailTemplate = $this->build->isSuccessful() ? 'Email/success' : 'Email/failed';
$view = new View($mailTemplate);
try {
$view = $this->getMailTemplate();
} catch (Exception $e) {
$this->phpci->log(
sprintf('Unknown mail template "%s", falling back to default.', $this->options['template']),
LogLevel::WARNING
);
$view = $this->getDefaultMailTemplate();
}
$view->build = $this->build;
$view->project = $this->build->getProject();
$body = $view->render();
$layout = new View('Email/layout');
$layout->build = $this->build;
$layout->project = $this->build->getProject();
$layout->content = $view->render();
$body = $layout->render();
$sendFailures = $this->sendSeparateEmails(
$addresses,
@ -97,7 +112,7 @@ class Email implements \PHPCI\Plugin
* @param string $body Email body
* @return array Array of failed addresses
*/
public function sendEmail($toAddress, $ccList, $subject, $body)
protected function sendEmail($toAddress, $ccList, $subject, $body)
{
$email = new EmailHelper();
@ -168,6 +183,7 @@ class Email implements \PHPCI\Plugin
/**
* Get the list of email addresses to CC.
*
* @return array
*/
protected function getCcAddresses()
@ -182,4 +198,30 @@ class Email implements \PHPCI\Plugin
return $ccAddresses;
}
/**
* Get the mail template used to sent the mail.
*
* @return View
*/
protected function getMailTemplate()
{
if (isset($this->options['template'])) {
return new View('Email/' . $this->options['template']);
}
return $this->getDefaultMailTemplate();
}
/**
* Get the default mail template.
*
* @return View
*/
protected function getDefaultMailTemplate()
{
$template = $this->build->isSuccessful() ? 'short' : 'long';
return new View('Email/' . $template);
}
}

View file

@ -0,0 +1,73 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Plugin;
use PHPCI\Builder;
use PHPCI\Model\Build;
use Mremi\Flowdock\Api\Push\Push;
use Mremi\Flowdock\Api\Push\TeamInboxMessage;
/**
* Flowdock Plugin
* @author Petr Cervenka <petr@nanosolutions.io>
* @package PHPCI
* @subpackage Plugins
*/
class FlowdockNotify implements \PHPCI\Plugin
{
private $api_key;
private $email;
const MESSAGE_DEFAULT = 'Build %BUILD% has finished for commit <a href="%COMMIT_URI%">%SHORT_COMMIT%</a>
(%COMMIT_EMAIL%)> on branch <a href="%BRANCH_URI%">%BRANCH%</a>';
/**
* Set up the plugin, configure options, etc.
* @param Builder $phpci
* @param Build $build
* @param array $options
* @throws \Exception
*/
public function __construct(Builder $phpci, Build $build, array $options = array())
{
$this->phpci = $phpci;
$this->build = $build;
if (!is_array($options) || !isset($options['api_key'])) {
throw new \Exception('Please define the api_key for Flowdock Notify plugin!');
}
$this->api_key = trim($options['api_key']);
$this->message = isset($options['message']) ? $options['message'] : self::MESSAGE_DEFAULT;
$this->email = isset($options['email']) ? $options['email'] : 'PHPCI';
}
/**
* Run the Flowdock plugin.
* @return bool
* @throws \Exception
*/
public function execute()
{
$message = $this->phpci->interpolate($this->message);
$successfulBuild = $this->build->isSuccessful() ? 'Success' : 'Failed';
$push = new Push($this->api_key);
$flowMessage = TeamInboxMessage::create()
->setSource("PHPCI")
->setFromAddress($this->email)
->setFromName($this->build->getProject()->getTitle())
->setSubject($successfulBuild)
->setTags(['#ci'])
->setLink($this->build->getBranchLink())
->setContent($message);
if (!$push->sendTeamInboxMessage($flowMessage, array('connect_timeout' => 5000, 'timeout' => 5000))) {
throw new \Exception(sprintf('Flowdock Failed: %s', $flowMessage->getResponseErrors()));
}
return true;
}
}

View file

@ -77,19 +77,56 @@ class Irc implements \PHPCI\Plugin
}
$sock = fsockopen($this->server, $this->port);
fputs($sock, 'USER ' . $this->nick . ' phptesting.org ' . $this->nick . ' :' . $this->nick . "\r\n");
fputs($sock, 'NICK ' . $this->nick . "\r\n");
fputs($sock, 'JOIN ' . $this->room . "\r\n");
fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n");
stream_set_timeout($sock, 1);
while (fgets($sock)) {
// We don't need to do anything,
// but the IRC server doesn't appear to post the message
// unless we wait for responses.
}
$connectCommands = array(
'USER ' . $this->nick . ' 0 * :' . $this->nick,
'NICK ' . $this->nick,
);
$this->executeIrcCommands($sock, $connectCommands);
$this->executeIrcCommand($sock, 'JOIN ' . $this->room);
$this->executeIrcCommand($sock, 'PRIVMSG ' . $this->room . ' :' . $msg);
fclose($sock);
return true;
}
/**
* @param resource $socket
* @param array $commands
* @return bool
*/
private function executeIrcCommands($socket, array $commands)
{
foreach ($commands as $command) {
fputs($socket, $command . "\n");
}
$pingBack = false;
// almost all servers expect pingback!
while ($response = fgets($socket)) {
$matches = array();
if (preg_match('/^PING \\:([A-Z0-9]+)/', $response, $matches)) {
$pingBack = $matches[1];
}
}
if ($pingBack) {
$command = 'PONG :' . $pingBack . "\n";
fputs($socket, $command);
}
}
/**
*
* @param resource $socket
* @param string $command
* @return bool
*/
private function executeIrcCommand($socket, $command)
{
return $this->executeIrcCommands($socket, array($command));
}
}

View file

@ -79,15 +79,10 @@ class Pdepend implements \PHPCI\Plugin
$pdepend = $this->phpci->findBinary('pdepend');
if (!$pdepend) {
$this->phpci->logFailure(Lang::get('could_not_find', 'pdepend'));
return false;
}
$cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"';
$this->removeBuildArtifacts();
// If we need to ignore directories
if (count($this->phpci->ignore)) {
$ignore = ' --ignore=' . implode(',', $this->phpci->ignore);

View file

@ -81,11 +81,6 @@ class Phing implements \PHPCI\Plugin
{
$phingExecutable = $this->phpci->findBinary('phing');
if (!$phingExecutable) {
$this->phpci->logFailure(Lang::get('could_not_find', 'phing'));
return false;
}
$cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath();
if ($this->getPropertyFile()) {

View file

@ -149,11 +149,6 @@ class PhpCodeSniffer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$phpcs = $this->phpci->findBinary('phpcs');
if (!$phpcs) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpcs'));
return false;
}
$this->phpci->logExecOutput(false);
$cmd = $phpcs . ' --report=json %s %s %s %s %s "%s"';

View file

@ -90,18 +90,13 @@ class PhpCpd implements \PHPCI\Plugin
$phpcpd = $this->phpci->findBinary('phpcpd');
if (!$phpcpd) {
$this->phpci->logFailure(Lang::get('could_not_find', 'phpcpd'));
return false;
}
$tmpfilename = tempnam('/tmp', 'phpcpd');
$cmd = $phpcpd . ' --log-pmd "%s" %s "%s"';
$success = $this->phpci->executeCommand($cmd, $tmpfilename, $ignore, $this->path);
print $this->phpci->getLastOutput();
list($errorCount, $data) = $this->processReport(file_get_contents($tmpfilename));
$this->build->storeMeta('phpcpd-warnings', $errorCount);
$this->build->storeMeta('phpcpd-data', $data);

View file

@ -69,11 +69,6 @@ class PhpCsFixer implements \PHPCI\Plugin
$phpcsfixer = $this->phpci->findBinary('php-cs-fixer');
if (!$phpcsfixer) {
$this->phpci->logFailure(Lang::get('could_not_find', 'php-cs-fixer'));
return false;
}
$cmd = $phpcsfixer . ' fix . %s %s %s';
$success = $this->phpci->executeCommand($cmd, $this->verbose, $this->diff, $this->level);

View file

@ -104,11 +104,6 @@ class PhpDocblockChecker implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
// Check that the binary exists:
$checker = $this->phpci->findBinary('phpdoccheck');
if (!$checker) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpdoccheck'));
return false;
}
// Build ignore string:
$ignore = '';
if (count($this->ignore)) {

View file

@ -80,11 +80,6 @@ class PhpLoc implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$phploc = $this->phpci->findBinary('phploc');
if (!$phploc) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phploc'));
return false;
}
$success = $this->phpci->executeCommand($phploc . ' %s "%s"', $ignore, $this->directory);
$output = $this->phpci->getLastOutput();

View file

@ -121,11 +121,6 @@ class PhpMessDetector implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$phpmdBinaryPath = $this->phpci->findBinary('phpmd');
if (!$phpmdBinaryPath) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpmd'));
return false;
}
$this->executePhpMd($phpmdBinaryPath);
list($errorCount, $data) = $this->processReport(trim($this->phpci->getLastOutput()));

View file

@ -78,11 +78,6 @@ class PhpParallelLint implements \PHPCI\Plugin
$phplint = $this->phpci->findBinary('parallel-lint');
if (!$phplint) {
$this->phpci->logFailure(Lang::get('could_not_find', 'parallel-lint'));
return false;
}
$cmd = $phplint . ' %s "%s"';
$success = $this->phpci->executeCommand(
$cmd,

View file

@ -59,11 +59,6 @@ class PhpSpec implements PHPCI\Plugin
$phpspec = $this->phpci->findBinary(array('phpspec', 'phpspec.php'));
if (!$phpspec) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpspec'));
return false;
}
$success = $this->phpci->executeCommand($phpspec . ' --format=junit --no-code-generation run');
$output = $this->phpci->getLastOutput();

View file

@ -197,15 +197,8 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
chdir($this->phpci->buildPath.'/'.$this->runFrom);
}
$phpunit = $this->phpci->findBinary('phpunit');
if (!$phpunit) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpunit'));
return false;
}
$cmd = $phpunit . ' --tap %s -c "%s" ' . $this->coverage . $this->path;
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath);
@ -232,11 +225,6 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$phpunit = $this->phpci->findBinary('phpunit');
if (!$phpunit) {
$this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpunit'));
return false;
}
$cmd = $phpunit . ' --tap %s "%s"';
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $directory);
chdir($curdir);

View file

@ -42,11 +42,6 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
*/
protected $allowed_errors;
/**
* @var int
*/
protected $allowed_warnings;
/**
* @var string, based on the assumption the root may not hold the code to be
* tested, extends the base path
@ -94,7 +89,6 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$this->directory = $phpci->buildPath;
$this->path = '';
$this->ignore = $this->phpci->ignore;
$this->allowed_warnings = 0;
$this->allowed_errors = 0;
$this->searches = array('TODO', 'FIXME', 'TO DO', 'FIX ME');
@ -103,9 +97,10 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
}
if (isset($options['zero_config']) && $options['zero_config']) {
$this->allowed_warnings = -1;
$this->allowed_errors = -1;
}
$this->setOptions($options);
}
/**
@ -114,7 +109,7 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
*/
protected function setOptions($options)
{
foreach (array('directory', 'path', 'ignore', 'allowed_warnings', 'allowed_errors') as $key) {
foreach (array('directory', 'path', 'ignore', 'allowed_errors') as $key) {
if (array_key_exists($key, $options)) {
$this->{$key} = $options[$key];
}

View file

@ -2,8 +2,12 @@
namespace PHPCI\Plugin\Util;
use b8\Store\Factory as StoreFactory;
use Exception;
use PHPCI\Helper\Lang;
use \PHPCI\Logging\BuildLogger;
use PHPCI\Logging\BuildLogger;
use PHPCI\Model\Build;
use PHPCI\Store\BuildStore;
/**
* Plugin Executor - Runs the configured plugins for a given build stage.
@ -21,14 +25,20 @@ class Executor
*/
protected $pluginFactory;
/**
* @var BuildStore
*/
protected $store;
/**
* @param Factory $pluginFactory
* @param BuildLogger $logger
*/
public function __construct(Factory $pluginFactory, BuildLogger $logger)
public function __construct(Factory $pluginFactory, BuildLogger $logger, BuildStore $store = null)
{
$this->pluginFactory = $pluginFactory;
$this->logger = $logger;
$this->store = $store ?: StoreFactory::getStore('Build');
}
/**
@ -48,22 +58,29 @@ class Executor
foreach ($config[$stage] as $plugin => $options) {
$this->logger->log(Lang::get('running_plugin', $plugin));
// Try and execute it:
if ($this->executePlugin($plugin, $options)) {
// Execution was successful:
$this->logger->logSuccess(Lang::get('plugin_success'));
} elseif ($stage == 'setup') {
// If we're in the "setup" stage, execution should not continue after
// a plugin has failed:
throw new \Exception('Plugin failed: ' . $plugin);
} else {
// If we're in the "test" stage and the plugin is not allowed to fail,
// then mark the build as failed:
if ($stage == 'test' && (!isset($options['allow_failures']) || !$options['allow_failures'])) {
$success = false;
}
$this->setPluginStatus($stage, $plugin, Build::STATUS_RUNNING);
// Try and execute it
if ($this->executePlugin($plugin, $options)) {
// Execution was successful
$this->logger->logSuccess(Lang::get('plugin_success'));
$this->setPluginStatus($stage, $plugin, Build::STATUS_SUCCESS);
} else {
// Execution failed
$this->logger->logFailure(Lang::get('plugin_failed'));
$this->setPluginStatus($stage, $plugin, Build::STATUS_FAILED);
if ($stage === 'setup') {
// If we're in the "setup" stage, execution should not continue after
// a plugin has failed:
throw new Exception('Plugin failed: ' . $plugin);
} elseif ($stage === 'test') {
// If we're in the "test" stage and the plugin is not allowed to fail,
// then mark the build as failed:
if (empty($options['allow_failures'])) {
$success = false;
}
}
}
}
@ -91,20 +108,62 @@ class Executor
return false;
}
$rtn = true;
// Try running it:
try {
// Build and run it
$obj = $this->pluginFactory->buildPlugin($class, $options);
if (!$obj->execute()) {
$rtn = false;
}
return $obj->execute();
} catch (\Exception $ex) {
$this->logger->logFailure(Lang::get('exception') . $ex->getMessage(), $ex);
$rtn = false;
return false;
}
}
/**
* Change the status of a plugin for a given stage.
*
* @param string $stage The builder stage.
* @param string $plugin The plugin name.
* @param int $status The new status.
*/
protected function setPluginStatus($stage, $plugin, $status)
{
$summary = $this->getBuildSummary();
if (!isset($summary[$stage][$plugin])) {
$summary[$stage][$plugin] = array();
}
return $rtn;
$summary[$stage][$plugin]['status'] = $status;
if ($status === Build::STATUS_RUNNING) {
$summary[$stage][$plugin]['started'] = time();
} elseif ($status >= Build::STATUS_SUCCESS) {
$summary[$stage][$plugin]['ended'] = time();
}
$this->setBuildSummary($summary);
}
/**
* Fetch the summary data of the current build.
*
* @return array
*/
private function getBuildSummary()
{
$build = $this->pluginFactory->getResourceFor('PHPCI\Model\Build');
$metas = $this->store->getMeta('plugin-summary', $build->getProjectId(), $build->getId());
return isset($metas[0]['meta_value']) ? $metas[0]['meta_value'] : array();
}
/**
* Sets the summary data of the current build.
*
* @param array summary
*/
private function setBuildSummary($summary)
{
$build = $this->pluginFactory->getResourceFor('PHPCI\Model\Build');
$this->store->setMeta($build->getProjectId(), $build->getId(), 'plugin-summary', json_encode($summary));
}
}

View file

@ -150,11 +150,11 @@ class Factory
}
/**
* @param null $type
* @param null $name
* @return null
* @param string $type
* @param string $name
* @return mixed
*/
private function getResourceFor($type = null, $name = null)
public function getResourceFor($type = null, $name = null)
{
$fullId = $this->getInternalID($type, $name);
if (isset($this->container[$fullId])) {
@ -185,7 +185,7 @@ class Factory
return $class->getName();
} elseif ($param->isArray()) {
return self::TYPE_ARRAY;
} elseif ($param->isCallable()) {
} elseif (is_callable($param)) {
return self::TYPE_CALLABLE;
} else {
return null;

View file

@ -2,7 +2,9 @@
namespace PHPCI\Plugin\Util;
use Exception;
use PHPCI\Helper\Lang;
use Symfony\Component\Yaml\Yaml;
/**
* Processes TAP format strings into usable test result data.
@ -10,18 +12,41 @@ use PHPCI\Helper\Lang;
*/
class TapParser
{
const TEST_COUNTS_PATTERN = '/([0-9]+)\.\.([0-9]+)/';
const TEST_LINE_PATTERN = '/(ok|not ok)\s+[0-9]+\s+\-\s+([^\n]+)::([^\n]+)/';
const TEST_MESSAGE_PATTERN = '/message\:\s+\'([^\']+)\'/';
const TEST_COVERAGE_PATTERN = '/Generating code coverage report/';
const TEST_SKIP_PATTERN = '/ok\s+[0-9]+\s+\-\s+#\s+SKIP/';
const TEST_COUNTS_PATTERN = '/^\d+\.\.(\d+)/';
const TEST_LINE_PATTERN = '/^(ok|not ok)(?:\s+\d+)?(?:\s+\-)?\s*(.*?)(?:\s*#\s*(skip|todo)\s*(.*))?\s*$/i';
const TEST_YAML_START = '/^(\s*)---/';
const TEST_DIAGNOSTIC = '/^#/';
/**
* @var string
*/
protected $tapString;
/**
* @var int
*/
protected $failures = 0;
/**
* @var array
*/
protected $lines;
/**
* @var integer
*/
protected $lineNumber;
/**
* @var integer
*/
protected $testCount;
/**
* @var array
*/
protected $results;
/**
* Create a new TAP parser for a given string.
* @param string $tapString The TAP format string to be parsed.
@ -38,81 +63,175 @@ class TapParser
{
// Split up the TAP string into an array of lines, then
// trim all of the lines so there's no leading or trailing whitespace.
$lines = explode("\n", $this->tapString);
$lines = array_map(function ($line) {
return trim($line);
}, $lines);
$this->lines = array_map('rtrim', explode("\n", $this->tapString));
$this->lineNumber = 0;
// Check TAP version:
$versionLine = array_shift($lines);
$this->testCount = false;
$this->results = array();
if ($versionLine != 'TAP version 13') {
throw new \Exception(Lang::get('tap_version'));
$header = $this->findTapLog();
$line = $this->nextLine();
if ($line === $header) {
throw new Exception("Duplicated TAP log, please check the configration.");
}
if (isset($lines[count($lines) - 1]) && preg_match(self::TEST_COVERAGE_PATTERN, $lines[count($lines) - 1])) {
array_pop($lines);
if ($lines[count($lines) - 1] == "") {
array_pop($lines);
while ($line !== false && ($this->testCount === false || count($this->results) < $this->testCount)) {
$this->parseLine($line);
$line = $this->nextLine();
}
if (count($this->results) !== $this->testCount) {
throw new Exception(Lang::get('tap_error'));
}
return $this->results;
}
/** Looks for the start of the TAP log in the string.
*
* @return string The TAP header line.
*
* @throws Exception if no TAP log is found or versions mismatch.
*/
protected function findTapLog()
{
// Look for the beggning of the TAP output
do {
$header = $this->nextLine();
} while ($header !== false && substr($header, 0, 12) !== 'TAP version ');
//
if ($header === false) {
throw new Exception('No TAP log found, please check the configuration.');
} elseif ($header !== 'TAP version 13') {
throw new Exception(Lang::get('tap_version'));
}
return $header;
}
/** Fetch the next line.
*
* @return string|false The next line or false if the end has been reached.
*/
protected function nextLine()
{
if ($this->lineNumber < count($this->lines)) {
return $this->lines[$this->lineNumber++];
}
return false;
}
/** Parse a single line.
*
* @param string $line
*/
protected function parseLine($line)
{
if (preg_match(self::TEST_COUNTS_PATTERN, $line, $matches)) {
$this->testCount = intval($matches[1]);
} elseif (preg_match(self::TEST_DIAGNOSTIC, $line)) {
return;
} elseif (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
$this->results[] = $this->processTestLine(
$matches[1],
isset($matches[2]) ? $matches[2] : '',
isset($matches[3]) ? $matches[3] : null,
isset($matches[4]) ? $matches[4] : null
);
} elseif (preg_match(self::TEST_YAML_START, $line, $matches)) {
$diagnostic = $this->processYamlBlock($matches[1]);
$test = array_pop($this->results);
if (isset($test['message'], $diagnostic['message'])) {
$test['message'] .= PHP_EOL . $diagnostic['message'];
unset($diagnostic['message']);
}
$this->results[] = array_replace($test, $diagnostic);
} else {
throw new Exception(sprintf('Incorrect TAP data, line %d: %s', $this->lineNumber, $line));
}
$matches = array();
$totalTests = 0;
if (preg_match(self::TEST_COUNTS_PATTERN, $lines[0], $matches)) {
array_shift($lines);
$totalTests = (int) $matches[2];
}
if (isset($lines[count($lines) - 1]) &&
preg_match(self::TEST_COUNTS_PATTERN, $lines[count($lines) - 1], $matches)) {
array_pop($lines);
$totalTests = (int) $matches[2];
}
$rtn = $this->processTestLines($lines);
if ($totalTests != count($rtn)) {
throw new \Exception(Lang::get('tap_error'));
}
return $rtn;
}
/**
* Process the individual test lines within a TAP string.
* @param $lines
* Process an individual test line.
*
* @param string $result
* @param string $message
* @param string $directive
* @param string $reason
*
* @return array
*/
protected function processTestLines($lines)
protected function processTestLine($result, $message, $directive, $reason)
{
$rtn = array();
$test = array(
'pass' => true,
'message' => $message,
'severity' => 'success',
);
foreach ($lines as $line) {
$matches = array();
if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
$ok = ($matches[1] == 'ok' ? true : false);
if (!$ok) {
$this->failures++;
}
$item = array(
'pass' => $ok,
'suite' => $matches[2],
'test' => $matches[3],
);
$rtn[] = $item;
} elseif (preg_match(self::TEST_SKIP_PATTERN, $line, $matches)) {
$rtn[] = array('message' => 'SKIP');
} elseif (preg_match(self::TEST_MESSAGE_PATTERN, $line, $matches)) {
$rtn[count($rtn) - 1]['message'] = $matches[1];
}
if ($result !== 'ok') {
$test['pass'] = false;
$test['severity'] = substr($message, 0, 6) === 'Error:' ? 'error' : 'fail';
$this->failures++;
}
return $rtn;
if ($directive) {
$test = $this->processDirective($test, $directive, $reason);
}
return $test;
}
/** Process an indented Yaml block.
*
* @param string $indent The block indentation to ignore.
*
* @return array The processed Yaml content.
*/
protected function processYamlBlock($indent)
{
$startLine = $this->lineNumber+1;
$endLine = $indent.'...';
$yamlLines = array();
do {
$line = $this->nextLine();
if ($line === false) {
throw new Exception(Lang::get('tap_error_endless_yaml', $startLine));
} elseif ($line === $endLine) {
break;
}
$yamlLines[] = substr($line, strlen($indent));
} while (true);
return Yaml::parse(join("\n", $yamlLines));
}
/** Process a TAP directive
*
* @param array $test
* @param string $directive
* @param string $reason
* @return array
*/
protected function processDirective($test, $directive, $reason)
{
$test['severity'] = strtolower($directive) === 'skip' ? 'skipped' : 'todo';
if (!empty($reason)) {
if (!empty($test['message'])) {
$test['message'] .= ', '.$test['severity'].': ';
}
$test['message'] .= $reason;
}
return $test;
}
/**

View file

@ -0,0 +1,113 @@
<?php
namespace PHPCI\Plugin\Util\TestResultParsers;
use PHPCI\Builder;
/**
* Class Codeception
*
* @author Adam Cooper <adam@networkpie.co.uk>
* @package PHPCI\Plugin\Util\TestResultParsers
*/
class Codeception implements ParserInterface
{
protected $phpci;
protected $resultsXml;
protected $results;
protected $totalTests;
protected $totalTimeTaken;
protected $totalFailures;
/**
* @param Builder $phpci
* @param $resultsXml
*/
public function __construct(Builder $phpci, $resultsXml)
{
$this->phpci = $phpci;
$this->resultsXml = $resultsXml;
$this->totalTests = 0;
}
/**
* @return array An array of key/value pairs for storage in the plugins result metadata
*/
public function parse()
{
$rtn = array();
$this->results = new \SimpleXMLElement($this->resultsXml);
// calculate total results
foreach ($this->results->testsuite as $testsuite) {
$this->totalTests += (int) $testsuite['tests'];
$this->totalTimeTaken += (float) $testsuite['time'];
$this->totalFailures += (int) $testsuite['failures'];
foreach ($testsuite->testcase as $testcase) {
$testresult = array(
'suite' => (string) $testsuite['name'],
'file' => str_replace($this->phpci->buildPath, '/', (string) $testcase['file']),
'name' => (string) $testcase['name'],
'feature' => (string) $testcase['feature'],
'assertions' => (int) $testcase['assertions'],
'time' => (float) $testcase['time']
);
if (isset($testcase['class'])) {
$testresult['class'] = (string) $testcase['class'];
}
// PHPUnit testcases does not have feature field. Use class::method instead
if (!$testresult['feature']) {
$testresult['feature'] = sprintf('%s::%s', $testresult['class'], $testresult['name']);
}
if (isset($testcase->failure)) {
$testresult['pass'] = false;
$testresult['message'] = (string) $testcase->failure;
} else {
$testresult['pass'] = true;
}
$rtn[] = $testresult;
}
}
return $rtn;
}
/**
* Get the total number of tests performed.
*
* @return int
*/
public function getTotalTests()
{
return $this->totalTests;
}
/**
* The time take to complete all tests
*
* @return mixed
*/
public function getTotalTimeTaken()
{
return $this->totalTimeTaken;
}
/**
* A count of the test failures
*
* @return mixed
*/
public function getTotalFailures()
{
return $this->totalFailures;
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace PHPCI\Plugin\Util\TestResultParsers;
interface ParserInterface
{
/**
* @return array An array of key/value pairs for storage in the plugins result metadata
*/
public function parse();
public function getTotalTests();
public function getTotalTimeTaken();
public function getTotalFailures();
}

View file

@ -61,8 +61,8 @@ class Wipe implements \PHPCI\Plugin
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
$success = $this->phpci->executeCommand($cmd, $this->directory);
return $this->phpci->executeCommand($cmd, $this->directory);
}
return $success;
return true;
}
}

View file

@ -132,8 +132,8 @@ class XMPP implements \PHPCI\Plugin
*/
public function findConfigFile()
{
if (file_exists('.sendxmpprc')) {
if (md5(file_get_contents('.sendxmpprc')) !== md5($this->getConfigFormat())) {
if (file_exists($this->phpci->buildPath . '/.sendxmpprc')) {
if (md5(file_get_contents($this->phpci->buildPath . '/.sendxmpprc')) !== md5($this->getConfigFormat())) {
return null;
}
@ -148,12 +148,7 @@ class XMPP implements \PHPCI\Plugin
*/
public function execute()
{
$sendxmpp = $this->phpci->findBinary('/usr/bin/sendxmpp');
if (!$sendxmpp) {
$this->phpci->logFailure('Could not find sendxmpp.');
return false;
}
$sendxmpp = $this->phpci->findBinary('sendxmpp');
/*
* Without recipients we can't send notification
@ -165,9 +160,10 @@ class XMPP implements \PHPCI\Plugin
/*
* Try to build conf file
*/
$config_file = $this->phpci->buildPath . '/.sendxmpprc';
if (is_null($this->findConfigFile())) {
file_put_contents('.sendxmpprc', $this->getConfigFormat());
chmod('.sendxmpprc', 0600);
file_put_contents($config_file, $this->getConfigFormat());
chmod($config_file, 0600);
}
/*
@ -178,7 +174,7 @@ class XMPP implements \PHPCI\Plugin
$tls = ' -t';
}
$message_file = uniqid('xmppmessage');
$message_file = $this->phpci->buildPath . '/' . uniqid('xmppmessage');
if ($this->buildMessage($message_file) === false) {
return false;
}
@ -186,10 +182,10 @@ class XMPP implements \PHPCI\Plugin
/*
* Send XMPP notification for all recipients
*/
$cmd = $sendxmpp . "%s -f .sendxmpprc -m %s %s";
$cmd = $sendxmpp . "%s -f %s -m %s %s";
$recipients = implode(' ', $this->recipients);
$success = $this->phpci->executeCommand($cmd, $tls, $message_file, $recipients);
$success = $this->phpci->executeCommand($cmd, $tls, $config_file, $message_file, $recipients);
print $this->phpci->getLastOutput();

View file

@ -0,0 +1,63 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\ProcessControl;
/**
* Construct an appropriate ProcessControl instance.
*
* @author Adirelle <adirelle@gmail.com>
*/
class Factory
{
/**
* ProcessControl singleton.
*
* @var ProcessControlInterface
*/
protected static $instance = null;
/**
* Returns the ProcessControl singleton.
*
* @return ProcessControlInterface
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = static::createProcessControl();
}
return static::$instance;
}
/**
* Create a ProcessControl depending on available extensions and the underlying OS.
*
* Check PosixProcessControl, WindowsProcessControl and UnixProcessControl, in that order.
*
* @return ProcessControlInterface
*
* @internal
*/
public static function createProcessControl()
{
switch(true) {
case PosixProcessControl::isAvailable():
return new PosixProcessControl();
case WindowsProcessControl::isAvailable():
return new WindowsProcessControl();
case UnixProcessControl::isAvailable():
return new UnixProcessControl();
}
throw new \Exception("No ProcessControl implementation available.");
}
}

View file

@ -0,0 +1,52 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\ProcessControl;
/**
* Control process using the POSIX extension.
*
* @author Adirelle <adirelle@gmail.com>
*/
class PosixProcessControl implements ProcessControlInterface
{
/**
*
* @param int $pid
* @return bool
*/
public function isRunning($pid)
{
// Signal "0" is not sent to the process, but posix_kill checks the process anyway;
return posix_kill($pid, 0);
}
/**
* Sends a TERMINATE or KILL signal to the process using posix_kill.
*
* @param int $pid
* @param bool $forcefully Whetehr to send TERMINATE (false) or KILL (true).
*/
public function kill($pid, $forcefully = false)
{
posix_kill($pid, $forcefully ? 9 : 15);
}
/**
* Check whether this posix_kill is available.
*
* @return bool
*
* @internal
*/
public static function isAvailable()
{
return function_exists('posix_kill');
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\ProcessControl;
/**
* A stateless service to check and kill system processes.
*
* @author Adirelle <adirelle@gmail.com>
*/
interface ProcessControlInterface
{
/** Checks if a process exists.
*
* @param int $pid The process identifier.
*
* @return boolean true is the process is running, else false.
*/
public function isRunning($pid);
/** Terminate a running process.
*
* @param int $pid The process identifier.
* @param bool $forcefully Whether to gently (false) or forcefully (true) terminate the process.
*/
public function kill($pid, $forcefully = false);
}

View file

@ -0,0 +1,54 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\ProcessControl;
/**
* Control processes using the "ps" and "kill" commands.
*
* @author Adirelle <adirelle@gmail.com>
*/
class UnixProcessControl implements ProcessControlInterface
{
/**
* Check process using the "ps" command.
*
* @param int $pid
* @return boolean
*/
public function isRunning($pid)
{
$output = $exitCode = null;
exec(sprintf("ps %d", $pid), $output, $exitCode);
return $exitCode === 0;
}
/**
* Sends a signal using the "kill" command.
*
* @param int $pid
* @param bool $forcefully
*/
public function kill($pid, $forcefully = false)
{
exec(sprintf("kill -%d %d", $forcefully ? 9 : 15, $pid));
}
/**
* Check whether the commands "ps" and "kill" are available.
*
* @return bool
*
* @internal
*/
public static function isAvailable()
{
return DIRECTORY_SEPARATOR === '/' && exec("which ps") && exec("which kill");
}
}

View file

@ -0,0 +1,54 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\ProcessControl;
/**
* Control processes using the "tasklist" and "taskkill" commands.
*
* @author Adirelle <adirelle@gmail.com>
*/
class WindowsProcessControl implements ProcessControlInterface
{
/**
* Check if the process is running using the "tasklist" command.
*
* @param type $pid
* @return bool
*/
public function isRunning($pid)
{
$lastLine = exec(sprintf('tasklist /fi "PID eq %d" /nh /fo csv 2>nul:', $pid));
$record = str_getcsv($lastLine);
return isset($record[1]) && intval($record[1]) === $pid;
}
/**
* Terminate the process using the "taskkill" command.
*
* @param type $pid
* @param bool $forcefully
*/
public function kill($pid, $forcefully = false)
{
exec(sprintf("taskkill /t /pid %d %s 2>nul:", $pid, $forcefully ? '/f' : ''));
}
/**
* Check whether the commands "tasklist" and "taskkill" are available.
*
* @return bool
*
* @internal
*/
public static function isAvailable()
{
return DIRECTORY_SEPARATOR === '\\' && exec("where tasklist") && exec("where taskkill");
}
}

View file

@ -9,6 +9,7 @@
namespace PHPCI\Service;
use PHPCI\Helper\Lang;
use PHPCI\Model\Build;
use PHPCI\Model\Project;
use PHPCI\Store\BuildStore;
@ -59,6 +60,7 @@ class BuildService
$build->setCommitId($commitId);
} else {
$build->setCommitId('Manual');
$build->setCommitMessage(Lang::get('manual_build'));
}
if (!is_null($branch)) {
@ -112,6 +114,7 @@ class BuildService
*/
public function deleteBuild(Build $build)
{
$build->removeBuildDirectory();
return $this->buildStore->delete($build);
}
}

View file

@ -0,0 +1,222 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Service;
use PHPCI\Model\Project;
use PHPCI\Model\Build;
/**
* Class BuildStatusService
* @package PHPCI\Service
*/
class BuildStatusService
{
/* @var BuildStatusService */
protected $prevService = null;
/* @var Project */
protected $project;
/** @var string */
protected $branch;
/* @var Build */
protected $build;
/** @var string */
protected $url;
/** @var array */
protected $finishedStatusIds = array(
Build::STATUS_SUCCESS,
Build::STATUS_FAILED,
);
/**
* @param $branch
* @param Project $project
* @param Build $build
* @param bool $isParent
*/
public function __construct(
$branch,
Project $project,
Build $build = null,
$isParent = false
) {
$this->project = $project;
$this->branch = $branch;
$this->build = $build;
if ($this->build) {
$this->loadParentBuild($isParent);
}
if (defined('PHPCI_URL')) {
$this->setUrl(PHPCI_URL);
}
}
/**
* @param $url
*/
public function setUrl($url)
{
$this->url = $url;
}
/**
* @return Build
*/
public function getBuild()
{
return $this->build;
}
/**
* @param bool $isParent
* @throws \Exception
*/
protected function loadParentBuild($isParent = true)
{
if ($isParent === false && !$this->isFinished()) {
$lastFinishedBuild = $this->project->getLatestBuild($this->branch, $this->finishedStatusIds);
if ($lastFinishedBuild) {
$this->prevService = new BuildStatusService(
$this->branch,
$this->project,
$lastFinishedBuild,
true
);
}
}
}
/**
* @return string
*/
public function getActivity()
{
if (in_array($this->build->getStatus(), $this->finishedStatusIds)) {
return 'Sleeping';
} elseif ($this->build->getStatus() == Build::STATUS_NEW) {
return 'Pending';
} elseif ($this->build->getStatus() == Build::STATUS_RUNNING) {
return 'Building';
}
return 'Unknown';
}
/**
* @return string
*/
public function getName()
{
return $this->project->getTitle() . ' / ' . $this->branch;
}
/**
* @return bool
*/
public function isFinished()
{
if (in_array($this->build->getStatus(), $this->finishedStatusIds)) {
return true;
}
return false;
}
/**
* @return null|Build
*/
public function getFinishedBuildInfo()
{
if ($this->isFinished()) {
return $this->build;
} elseif ($this->prevService) {
return $this->prevService->getBuild();
}
return null;
}
/**
* @return int|string
*/
public function getLastBuildLabel()
{
if ($buildInfo = $this->getFinishedBuildInfo()) {
return $buildInfo->getId();
}
return '';
}
/**
* @return string
*/
public function getLastBuildTime()
{
$dateFormat = 'Y-m-d\\TH:i:sO';
if ($buildInfo = $this->getFinishedBuildInfo()) {
return ($buildInfo->getFinished()) ? $buildInfo->getFinished()->format($dateFormat) : '';
}
return '';
}
/**
* @param Build $build
* @return string
*/
public function getBuildStatus(Build $build)
{
switch ($build->getStatus()) {
case Build::STATUS_SUCCESS:
return 'Success';
case Build::STATUS_FAILED:
return 'Failure';
}
return 'Unknown';
}
/**
* @return string
*/
public function getLastBuildStatus()
{
if ($build = $this->getFinishedBuildInfo()) {
return $this->getBuildStatus($build);
}
return '';
}
/**
* @return string
*/
public function getBuildUrl()
{
return $this->url . 'build/view/' . $this->build->getId();
}
/**
* @return array
*/
public function toArray()
{
if (!$this->build) {
return array();
}
return array(
'name' => $this->getName(),
'activity' => $this->getActivity(),
'lastBuildLabel' => $this->getLastBuildLabel(),
'lastBuildStatus' => $this->getLastBuildStatus(),
'lastBuildTime' => $this->getLastBuildTime(),
'webUrl' => $this->getBuildUrl(),
);
}
}

View file

@ -59,7 +59,7 @@ class UserStoreBase extends Store
/**
* Returns a User model by Email.
* @param mixed $value
* @param string $value
* @param string $useConnection
* @throws HttpException
* @return \@appNamespace\Model\User|null
@ -82,4 +82,30 @@ class UserStoreBase extends Store
return null;
}
/**
* Returns a User model by Email.
* @param string $value
* @param string $useConnection
* @throws HttpException
* @return \@appNamespace\Model\User|null
*/
public function getByLoginOrEmail($value, $useConnection = 'read')
{
if (is_null($value)) {
throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.');
}
$query = 'SELECT * FROM `user` WHERE `name` = :value OR `email` = :value LIMIT 1';
$stmt = Database::getConnection($useConnection)->prepare($query);
$stmt->bindValue(':value', $value);
if ($stmt->execute()) {
if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) {
return new User($data);
}
}
return null;
}
}

View file

@ -107,6 +107,27 @@ class BuildStore extends BuildStoreBase
}
}
/**
* Returns all registered branches for project
*
* @param $projectId
* @return array
* @throws \Exception
*/
public function getBuildBranches($projectId)
{
$query = 'SELECT DISTINCT `branch` FROM `build` WHERE `project_id` = :project_id';
$stmt = Database::getConnection('read')->prepare($query);
$stmt->bindValue(':project_id', $projectId);
if ($stmt->execute()) {
$res = $stmt->fetchAll(\PDO::FETCH_COLUMN);
return $res;
} else {
return array();
}
}
/**
* Return build metadata by key, project and optionally build id.
* @param $key
@ -144,7 +165,9 @@ class BuildStore extends BuildStoreBase
$stmt->bindValue(':projectId', (int)$projectId, \PDO::PARAM_INT);
$stmt->bindValue(':buildId', (int)$buildId, \PDO::PARAM_INT);
$stmt->bindValue(':numResults', (int)$numResults, \PDO::PARAM_INT);
$stmt->bindValue(':branch', $branch, \PDO::PARAM_STR);
if (!is_null($branch)) {
$stmt->bindValue(':branch', $branch, \PDO::PARAM_STR);
}
if ($stmt->execute()) {
$rtn = $stmt->fetchAll(\PDO::FETCH_ASSOC);

View file

@ -39,6 +39,7 @@ switch($build->getStatus())
?>
<tr class="<?php print $cls; ?>">
<td><a href="<?php echo PHPCI_URL ?>build/view/<?php print $build->getId(); ?>">#<?php print str_pad($build->getId(), 6, '0', STR_PAD_LEFT); ?></a></td>
<td><?php print $build->getCreated()->format('Y-m-d H:i:s'); ?></td>
<td><a href="<?php echo PHPCI_URL ?>project/view/<?php print $build->getProjectId(); ?>">
<?php
if (is_object($build->getProject())) {

View file

@ -1,15 +0,0 @@
<div style="background: #900; padding: 25px;">
<div style="background: #fff; padding: 15px; border-radius: 5px">
<div style="font-family: arial, verdana, sans-serif; font-size: 25px; margin-bottom: 15px">
<?php print $project->getTitle(); ?> - Build #<?php print $build->getId(); ?>
</div>
<div style="font-family: arial, verdana, sans-serif; font-size: 15px">
<p>Your commit <strong><?php print $build->getCommitId(); ?></strong> caused a failed build in project <strong><?php print $project->getTitle(); ?></strong>.</p>
<p style="margin: 10px; background: #fafafa"><?php print $build->getCommitMessage(); ?></p>
<p>Please review <a href="<?php print $build->getCommitLink(); ?>">your commit</a> and the <a href="<?php print PHPCI_URL . 'build/view/' . $build->getId(); ?>">build log</a>.</p>
</div>
</div>
</div>

View file

@ -0,0 +1,28 @@
<html>
<head>
<style>
<?php include(__DIR__ . '/../../../public/assets/css/ansi-colors.css'); ?>
body { font-family: arial, verdana, sans-serif; font-size: 15px; }
header { font-size: 25px; margin-bottom: 15px; }
</style>
</head>
<body>
<div style="background: #<?php print $build->isSuccessful() ? '090' : '900'; ?>; padding: 25px;">
<div style="background: #fff; padding: 15px; border-radius: 5px">
<header><?php print $project->getTitle(); ?> - Build #<?php print $build->getId(); ?></header>
<div>
<p>
Your commit <strong><?php print $build->getCommitId(); ?></strong> generated a
<?php print $build->isSuccessful() ? 'successful' : 'failed'; ?> build in project
<strong><?php print $project->getTitle(); ?></strong>.
</p>
<?php print $content; ?>
</div>
<footer>
You can review <a href="<?php print $build->getCommitLink(); ?>">your commit</a>
and the <a href="<?php print PHPCI_URL . 'build/view/' . $build->getId(); ?>">build log</a>.
</footer>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,2 @@
<p style="margin: 10px; background: #fafafa"><?php print $build->getCommitMessage(); ?></p>
<pre class="ansi_color_bg_black ansi_color_fg_white" style="padding: 4px"><?php print \PHPCI\Helper\AnsiConverter::convert($build->getLog()); ?></pre>

View file

@ -0,0 +1 @@
<p style="margin: 10px; background: #fafafa"><?php print $build->getCommitMessage(); ?></p>

View file

@ -1,15 +0,0 @@
<div style="background: #090; padding: 25px;">
<div style="background: #fff; padding: 15px; border-radius: 5px">
<div style="font-family: arial, verdana, sans-serif; font-size: 25px; margin-bottom: 15px">
<?php print $project->getTitle(); ?> - Build #<?php print $build->getId(); ?>
</div>
<div style="font-family: arial, verdana, sans-serif; font-size: 15px">
<p>Your commit <strong><?php print $build->getCommitId(); ?></strong> genrate a successfull build in project <strong><?php print $project->getTitle(); ?></strong>.</p>
<p style="margin: 10px; background: #fafafa"><?php print $build->getCommitMessage(); ?></p>
<pre><?php print $build->getLog(); ?></pre>
<p>You can review <a href="<?php print $build->getCommitLink(); ?>">your commit</a> and the <a href="<?php print PHPCI_URL . 'build/view/' . $build->getId(); ?>">build log</a>.</p>
</div>
</div>
</div>

View file

@ -6,7 +6,7 @@
<div class="clearfix" style="margin-bottom: 20px;">
<div class="pull-right btn-group">
<a class="btn btn-success" href="<?php print PHPCI_URL . 'project/build/' . $project->getId(); ?>">
<a class="btn btn-success" href="<?php print PHPCI_URL . 'project/build/' . $project->getId(); ?>/<?php echo urlencode($branch) ?>">
<?php Lang::out('build_now'); ?>
</a>
@ -36,13 +36,13 @@
<div class="col-lg-9 col-md-8 col-sm-8">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title"><?php Lang::out('builds'); ?></h3>
<h3 class="box-title"><?php Lang::out('builds'); ?> (<?php print $total; ?>)</h3>
</div>
<table class="table">
<thead>
<tr>
<th><?php Lang::out('id'); ?></th>
<th><?php Lang::out('date'); ?></th>
<th><?php Lang::out('project'); ?></th>
<th class="hidden-md hidden-sm hidden-xs"><?php Lang::out('commit'); ?></th>
<th><?php Lang::out('branch'); ?></th>
@ -153,4 +153,4 @@ if ($page < $pages - 1) {
print '</ul></div>';
?>
?>

View file

@ -59,20 +59,20 @@
$returnTo = PHPCI_URL . 'settings/github-callback';
$githubUri = 'https://github.com/login/oauth/authorize?client_id='.$id.'&scope=repo&redirect_uri=' . $returnTo;
?>
<?php if (!empty($id) && empty($settings['phpci']['github']['token'])): ?>
<p class="alert alert-warning clearfix">
<?php Lang::out('github_sign_in', $githubUri); ?>
</p>
<?php endif; ?>
<?php if (!empty($id)): ?>
<?php if (empty($githubUser['name']) || empty($settings['phpci']['github']['token'])): ?>
<p class="alert alert-warning clearfix">
<?php Lang::out('github_sign_in', $githubUri); ?>
</p>
<?php else: ?>
<p class="alert alert-success">
<?php Lang::out('github_phpci_linked'); ?>
<?php if (!empty($id) && !empty($settings['phpci']['github']['token'])): ?>
<p class="alert alert-success">
<?php Lang::out('github_phpci_linked'); ?>
<strong>
<a href="<?php echo $githubUser['html_url']; ?>"><?php echo $githubUser['name']; ?></a>
</strong>
</p>
<strong>
<a href="<?php echo $githubUser['html_url']; ?>"><?php echo $githubUser['name']; ?></a>
</strong>
</p>
<?php endif; ?>
<?php endif; ?>
</div>

View file

@ -123,7 +123,7 @@ foreach($projects as $project):
<i class="fa fa-<?php print $project->getIcon(); ?>"></i>
</div>
<a href="<?php print PHPCI_URL; ?>project/view/<?php print $project->getId(); ?>" class="small-box-footer small-box-footer-project">
<?php Lang::out('view_project'); ?> <i class="fa fa-arrow-circle-right"></i>
<?php Lang::out('view_project'); ?> (<?php print $counts[$project->getId()]; ?>) <i class="fa fa-arrow-circle-right"></i>
</a>
<?php endif; ?>

View file

@ -13,6 +13,7 @@
<link href="<?php print PHPCI_URL; ?>assets/css/datepicker/datepicker3.css" rel="stylesheet" type="text/css" />
<link href="<?php print PHPCI_URL; ?>assets/css/daterangepicker/daterangepicker-bs3.css" rel="stylesheet" type="text/css" />
<link href="<?php print PHPCI_URL; ?>assets/css/ansi-colors.css" rel="stylesheet" type="text/css" />
<!-- Theme style -->
<link href="<?php print PHPCI_URL; ?>assets/css/AdminLTE.css" rel="stylesheet" type="text/css" />
@ -137,7 +138,7 @@
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="active">
<li<?php print (array_key_exists('archived', $_GET) ? '' : ' class="active"'); ?>>
<a href="<?php print PHPCI_URL; ?>">
<i class="fa fa-dashboard"></i> <span><?php Lang::out('dashboard'); ?></span>
</a>
@ -237,8 +238,8 @@
</li>
<?php endif; ?>
<li class="archive">
<a href="<?php print PHPCI_URL; ?>?archived">
<li class="archive<?php print (array_key_exists('archived', $_GET) ? ' active' : ''); ?>">
<a href="<?php print PHPCI_URL . (array_key_exists('archived', $_GET) ? '' : '?archived'); ?>">
<i class="fa fa-archive"></i> <span><?php Lang::out('archived'); ?></span>
</a>
</li>

View file

@ -1,13 +1,14 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link http://www.phptesting.org/
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Plugin\Tests\Command;
namespace Tests\PHPCI\Plugin\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
@ -34,17 +35,17 @@ class CreateAdminCommandTest extends \PHPUnit_Framework_TestCase
parent::setup();
$this->command = $this->getMockBuilder('PHPCI\\Command\\CreateAdminCommand')
->setConstructorArgs([$this->getMock('PHPCI\\Store\\UserStore')])
->setMethods(['reloadConfig'])
->setConstructorArgs(array($this->getMock('PHPCI\\Store\\UserStore')))
->setMethods(array('reloadConfig'))
->getMock()
;
$this->dialog = $this->getMockBuilder('Symfony\\Component\\Console\\Helper\\DialogHelper')
->setMethods([
->setMethods(array(
'ask',
'askAndValidate',
'askHiddenResponse',
])
))
->getMock()
;
@ -71,7 +72,7 @@ class CreateAdminCommandTest extends \PHPUnit_Framework_TestCase
$this->dialog->expects($this->at(2))->method('askHiddenResponse')->will($this->returnValue('foobar123'));
$commandTester = $this->getCommandTester();
$commandTester->execute([]);
$commandTester->execute(array());
$this->assertEquals('User account created!' . PHP_EOL, $commandTester->getDisplay());
}

View file

@ -0,0 +1,86 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link http://www.phptesting.org/
*/
namespace Tests\PHPCI\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
class CreateBuildCommandTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \PHPCI\Command\CreateAdminCommand|\PHPUnit_Framework_MockObject_MockObject
*/
protected $command;
/**
* @var \Symfony\Component\Console\Application|\PHPUnit_Framework_MockObject_MockObject
*/
protected $application;
public function setup()
{
parent::setup();
$projectMock = $this->getMock('PHPCI\\Model\\Project');
$projectStoreMock = $this->getMockBuilder('PHPCI\\Store\\ProjectStore')
->getMock();
$projectStoreMock->method('getById')
->will($this->returnValueMap(array(
array(1, 'read', $projectMock),
array(2, 'read', null),
)));
$buildServiceMock = $this->getMockBuilder('PHPCI\\Service\\BuildService')
->disableOriginalConstructor()
->getMock();
$buildServiceMock->method('createBuild')
->withConsecutive(
array($projectMock, null, null, null, null, null),
array($projectMock, '92c8c6e', null, null, null, null),
array($projectMock, null, 'master', null, null, null)
);
$this->command = $this->getMockBuilder('PHPCI\\Command\\CreateBuildCommand')
->setConstructorArgs(array($projectStoreMock, $buildServiceMock))
->setMethods(array('reloadConfig'))
->getMock();
$this->application = new Application();
}
protected function getCommandTester()
{
$this->application->add($this->command);
$command = $this->application->find('phpci:create-build');
$commandTester = new CommandTester($command);
return $commandTester;
}
public function testExecute()
{
$commandTester = $this->getCommandTester();
$commandTester->execute(array('projectId' => 1));
$commandTester->execute(array('projectId' => 1, '--commit' => '92c8c6e'));
$commandTester->execute(array('projectId' => 1, '--branch' => 'master'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testExecuteWithUnknowProjectId()
{
$commandTester = $this->getCommandTester();
$commandTester->execute(array('projectId' => 2));
}
}

View file

@ -1,16 +1,23 @@
<?php
namespace PHPCI\Plugin\Tests\Command;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Command;
use Symfony\Component\Console\Application;
use Prophecy\PhpUnit\ProphecyTestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Helper\HelperSet;
class InstallCommandTest extends ProphecyTestCase
class InstallCommandTest extends \PHPUnit_Framework_TestCase
{
protected $config;
protected $admin;
public $config;
public $admin;
protected $application;
public function setup()
@ -55,22 +62,26 @@ class InstallCommandTest extends ProphecyTestCase
'setupDatabase',
'createAdminUser',
'writeConfigFile',
'checkRequirements',
))
->getMock();
$self = $this;
$command->expects($this->once())->method('verifyNotInstalled')->willReturn(true);
$command->expects($this->once())->method('verifyDatabaseDetails')->willReturn(true);
$command->expects($this->once())->method('setupDatabase')->willReturn(true);
$command->expects($this->once())->method('createAdminUser')->will(
$this->returnCallback(function ($adm) {// use (&$admin) {
$this->admin = $adm;
$this->returnCallback(function ($adm) use ($self) {
$self->admin = $adm;
})
);
$command->expects($this->once())->method('writeConfigFile')->will(
$this->returnCallback(function ($cfg) { //use (&$config) {
$this->config = $cfg;
$this->returnCallback(function ($cfg) use ($self) {
$self->config = $cfg;
})
);
$command->expects($this->once())->method('checkRequirements');
return $command;
}

View file

@ -0,0 +1,41 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Controller;
use PHPCI\Controller\WebhookController;
class WebhookControllerTest extends \PHPUnit_Framework_TestCase
{
public function test_wrong_action_name_return_json_with_error()
{
$webController = new WebhookController(
$this->prophesize('b8\Config')->reveal(),
$this->prophesize('b8\Http\Request')->reveal(),
$this->prophesize('b8\Http\Response')->reveal()
);
$error = $webController->handleAction('test', []);
$this->assertInstanceOf('b8\Http\Response\JsonResponse', $error);
$responseData = $error->getData();
$this->assertEquals(500, $responseData['code']);
$this->assertEquals('failed', $responseData['body']['status']);
$this->assertEquals('application/json', $responseData['headers']['Content-Type']);
// @todo: we can't text the result is JSON file with
// $this->assertJson((string) $error);
// since the flush method automatically add the header and break the
// testing framework.
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Helper;
use PHPCI\Helper\AnsiConverter;
use PHPUnit_Framework_TestCase;
class AnsiConverterTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
$this->markTestSkipped('External library do not support PHP 5.3');
}
}
public function testConvert_convertToHtml()
{
$input = "\e[31mThis is red !\e[0m";
$expectedOutput = '<span class="ansi_color_bg_black ansi_color_fg_red">This is red !</span>';
$actualOutput = AnsiConverter::convert($input);
$this->assertEquals($expectedOutput, $actualOutput);
}
}

View file

@ -1,11 +1,18 @@
<?php
namespace PHPCI\Plugin\Tests\Helper;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Helper;
use PHPCI\Helper\BuildInterpolator;
use Prophecy\PhpUnit\ProphecyTestCase;
class BuildInterpolatorTest extends ProphecyTestCase
class BuildInterpolatorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var BuildInterpolator
@ -46,4 +53,4 @@ class BuildInterpolatorTest extends ProphecyTestCase
$this->assertEquals($expectedOutput, $actualOutput);
}
}

View file

@ -1,11 +1,18 @@
<?php
namespace PHPCI\Plugin\Tests\Helper;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Helper;
use PHPCI\Helper\UnixCommandExecutor;
use \Prophecy\PhpUnit\ProphecyTestCase;
class CommandExecutorTest extends ProphecyTestCase
class CommandExecutorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var UnixCommandExecutor
@ -14,9 +21,14 @@ class CommandExecutorTest extends ProphecyTestCase
protected function setUp()
{
if (IS_WIN) {
$this->markTestSkipped("Cannot test UnixCommandExecutor on ".PHP_OS);
return;
}
parent::setUp();
$mockBuildLogger = $this->prophesize('PHPCI\Logging\BuildLogger');
$this->testedExecutor = new UnixCommandExecutor($mockBuildLogger->reveal(), __DIR__ . "/");
$class = IS_WIN ? 'PHPCI\Helper\WindowsCommandExecutor' : 'PHPCI\Helper\UnixCommandExecutor';
$this->testedExecutor = new $class($mockBuildLogger->reveal(), __DIR__ . "/");
}
public function testGetLastOutput_ReturnsOutputOfCommand()
@ -52,4 +64,20 @@ class CommandExecutorTest extends ProphecyTestCase
$returnValue = $this->testedExecutor->findBinary($thisFileName);
$this->assertEquals(__DIR__ . "/" . $thisFileName, $returnValue);
}
}
/**
* @expectedException \Exception
* @expectedMessageRegex WorldWidePeace
*/
public function testFindBinary_ThrowsWhenNotFound()
{
$thisFileName = "WorldWidePeace";
$this->testedExecutor->findBinary($thisFileName);
}
public function testFindBinary_ReturnsNullWihQuietArgument()
{
$thisFileName = "WorldWidePeace";
$this->assertNull($this->testedExecutor->findBinary($thisFileName, true));
}
}

View file

@ -1,12 +1,19 @@
<?php
namespace PHPCI\Tests\Helper;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Helper;
use DateTime;
use PHPCI\Helper\Lang;
use Prophecy\PhpUnit\ProphecyTestCase;
class LangTest extends ProphecyTestCase
class LangTest extends \PHPUnit_Framework_TestCase
{
public function testLang_UsePassedParameters()
{

View file

@ -1,13 +1,14 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link http://www.phptesting.org/
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Service\Tests;
namespace tests\PHPCI\Service;
use PHPCI\Helper\MailerFactory;

View file

@ -1,13 +1,20 @@
<?php
namespace PHPCI\Plugin\Tests\Helper;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Helper;
use PHPCI\Logging\BuildLogger;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTestCase;
use Psr\Log\LogLevel;
class BuildLoggerTest extends ProphecyTestCase
class BuildLoggerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var BuildLogger
@ -104,4 +111,4 @@ class BuildLoggerTest extends ProphecyTestCase
$this->testedBuildLogger->logFailure($message, $exception);
}
}

View file

@ -1,6 +1,14 @@
<?php
namespace PHPCI\Plugin\Tests\Helper;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Helper;
use \PHPCI\Logging\LoggerConfig;
@ -73,5 +81,14 @@ class LoggerConfigTest extends \PHPUnit_Framework_TestCase
$this->assertSame($expectedHandler, $actualHandler);
$this->assertNotSame($alternativeHandler, $actualHandler);
}
public function testGetFor_SameInstance()
{
$config = new LoggerConfig(array());
$logger1 = $config->getFor("something");
$logger2 = $config->getFor("something");
$this->assertSame($logger1, $logger2);
}
}

View file

@ -1,13 +1,15 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link http://www.phptesting.org/
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Model\Tests;
namespace Tests\PHPCI\Model;
use PHPCI\Model\Build;
use PHPCI\Model;

View file

@ -1,13 +1,15 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2014, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link http://www.phptesting.org/
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Model\Tests;
namespace Tests\PHPCI\Model;
use PHPCI\Model\Project;
use PHPCI\Model;

View file

@ -1,13 +1,14 @@
<?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/
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace PHPCI\Plugin\Tests;
namespace Tests\PHPCI\Plugin;
use PHPCI\Plugin\Email as EmailPlugin;
use PHPCI\Model\Build;
@ -42,22 +43,23 @@ class EmailTest extends \PHPUnit_Framework_TestCase
/**
* @var int buildStatus
*/
protected $buildStatus;
public $buildStatus;
/**
* @var array $message;
*/
protected $message;
public $message;
/**
* @var bool $mailDelivered
*/
protected $mailDelivered;
public $mailDelivered;
public function setUp()
{
$this->message = array();
$this->mailDelivered = true;
$self = $this;
$this->mockProject = $this->getMock(
'\PHPCI\Model\Project',
@ -85,8 +87,8 @@ class EmailTest extends \PHPUnit_Framework_TestCase
$this->mockBuild->expects($this->any())
->method('getStatus')
->will($this->returnCallback(function () {
return $this->buildStatus;
->will($this->returnCallback(function () use ($self) {
return $self->buildStatus;
}));
$this->mockBuild->expects($this->any())
@ -138,6 +140,8 @@ class EmailTest extends \PHPUnit_Framework_TestCase
// Reset current message.
$this->message = array();
$self = $this;
$this->testedEmailPlugin = $this->getMock(
'\PHPCI\Plugin\Email',
array('sendEmail'),
@ -150,13 +154,13 @@ class EmailTest extends \PHPUnit_Framework_TestCase
$this->testedEmailPlugin->expects($this->any())
->method('sendEmail')
->will($this->returnCallback(function ($to, $cc, $subject, $body) {
$this->message['to'][] = $to;
$this->message['cc'] = $cc;
$this->message['subject'] = $subject;
$this->message['body'] = $body;
->will($this->returnCallback(function ($to, $cc, $subject, $body) use ($self) {
$self->message['to'][] = $to;
$self->message['cc'] = $cc;
$self->message['subject'] = $subject;
$self->message['body'] = $body;
return $this->mailDelivered;
return $self->mailDelivered;
}));
}
@ -345,7 +349,7 @@ class EmailTest extends \PHPUnit_Framework_TestCase
$this->testedEmailPlugin->execute();
$this->assertContains('Passing', $this->message['subject']);
$this->assertContains('successfull', $this->message['body']);
$this->assertContains('successful', $this->message['body']);
}
/**

View file

@ -1,5 +1,14 @@
<?php
namespace PHPCI\Plugin\Tests;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin;
use PHPCI\Plugin\Phar as PharPlugin;
use Phar as PHPPhar;
@ -78,8 +87,7 @@ class PharTest extends \PHPUnit_Framework_TestCase
protected function checkReadonly()
{
if (ini_get('phar.readonly')) {
$this->markTestSkipped();
throw new RuntimeException('Readonly Phar');
$this->markTestSkipped('phar writing disabled in php.ini.');
}
}

View file

@ -1,6 +1,14 @@
<?php
namespace PHPCI\Plugin\Tests\Util;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Util;
use PHPCI\Plugin\Util\ComposerPluginInformation;
@ -48,4 +56,4 @@ class ComposerPluginInformationTest extends \PHPUnit_Framework_TestCase
$this->assertContainsOnly("string", $classes);
}
}

View file

@ -1,4 +1,13 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
return function (PHPCI\Plugin\Util\Factory $factory) {
$factory->registerResource(
// This function will be called when the resource is needed.
@ -10,4 +19,4 @@ return function (PHPCI\Plugin\Util\Factory $factory) {
"requiredArgument",
null
);
};
};

View file

@ -1,78 +0,0 @@
<?php
namespace PHPCI\Plugin\Tests\Util;
use PHPCI\Builder;
use PHPCI\Model\Build;
use PHPCI\Plugin;
class ExamplePluginWithNoConstructorArgs implements Plugin
{
public function execute()
{
}
}
class ExamplePluginWithSingleOptionalArg implements Plugin
{
function __construct($optional = null)
{
}
public function execute()
{
}
}
class ExamplePluginWithSingleRequiredArg implements Plugin
{
public $RequiredArgument;
function __construct($requiredArgument)
{
$this->RequiredArgument = $requiredArgument;
}
public function execute()
{
}
}
class ExamplePluginWithSingleTypedRequiredArg implements Plugin
{
public $RequiredArgument;
function __construct(\stdClass $requiredArgument)
{
$this->RequiredArgument = $requiredArgument;
}
public function execute()
{
}
}
class ExamplePluginFull implements Plugin {
public $Options;
public function __construct(
Builder $phpci,
Build $build,
array $options = array()
)
{
$this->Options = $options;
}
public function execute()
{
}
}

View file

@ -1,14 +1,19 @@
<?php
namespace PHPCI\Plugin\Tests\Util;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
require_once __DIR__ . "/ExamplePlugins.php";
namespace Tests\PHPCI\Plugin\Util;
use PHPCI\Plugin\Util\Executor;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTestCase;
class ExecutorTest extends ProphecyTestCase
class ExecutorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Executor
@ -19,12 +24,19 @@ class ExecutorTest extends ProphecyTestCase
protected $mockFactory;
protected $mockStore;
protected function setUp()
{
parent::setUp();
$this->mockBuildLogger = $this->prophesize('\PHPCI\Logging\BuildLogger');
$this->mockFactory = $this->prophesize('\PHPCI\Plugin\Util\Factory');
$this->testedExecutor = new Executor($this->mockFactory->reveal(), $this->mockBuildLogger->reveal());
$this->mockStore = $this->prophesize('\PHPCI\Store\BuildStore');
$this->testedExecutor = new Executor(
$this->mockFactory->reveal(),
$this->mockBuildLogger->reveal(),
$this->mockStore->reveal()
);
}
public function testExecutePlugin_AssumesPHPCINamespaceIfNoneGiven()
@ -43,25 +55,26 @@ class ExecutorTest extends ProphecyTestCase
public function testExecutePlugin_KeepsCalledNameSpace()
{
$options = array();
$pluginName = 'ExamplePluginFull';
$pluginNamespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$pluginClass = $this->getFakePluginClassName('ExamplePluginFull');
$this->mockFactory->buildPlugin($pluginNamespace . $pluginName, $options)
$this->mockFactory->buildPlugin($pluginClass, $options)
->shouldBeCalledTimes(1)
->willReturn($this->prophesize('PHPCI\Plugin')->reveal());
$this->testedExecutor->executePlugin($pluginNamespace . $pluginName, $options);
$this->testedExecutor->executePlugin($pluginClass, $options);
}
public function testExecutePlugin_CallsExecuteOnFactoryBuildPlugin()
{
$options = array();
$pluginName = 'PhpUnit';
$build = new \PHPCI\Model\Build();
$mockPlugin = $this->prophesize('PHPCI\Plugin');
$mockPlugin->execute()->shouldBeCalledTimes(1);
$this->mockFactory->buildPlugin(Argument::any(), Argument::any())->willReturn($mockPlugin->reveal());
$this->mockFactory->getResourceFor('PHPCI\Model\Build')->willReturn($build);
$this->testedExecutor->executePlugin($pluginName, $options);
}
@ -115,6 +128,7 @@ class ExecutorTest extends ProphecyTestCase
{
$phpUnitPluginOptions = array();
$behatPluginOptions = array();
$build = new \PHPCI\Model\Build();
$config = array(
'stageOne' => array(
@ -130,7 +144,7 @@ class ExecutorTest extends ProphecyTestCase
$this->mockFactory->buildPlugin($pluginNamespace . 'PhpUnit', $phpUnitPluginOptions)
->willReturn($mockPhpUnitPlugin->reveal());
$this->mockFactory->getResourceFor('PHPCI\Model\Build')->willReturn($build);
$mockBehatPlugin = $this->prophesize('PHPCI\Plugin');
$mockBehatPlugin->execute()->shouldBeCalledTimes(1)->willReturn(true);
@ -138,9 +152,14 @@ class ExecutorTest extends ProphecyTestCase
$this->mockFactory->buildPlugin($pluginNamespace . 'Behat', $behatPluginOptions)
->willReturn($mockBehatPlugin->reveal());
$this->testedExecutor->executePlugins($config, 'stageOne');
}
protected function getFakePluginClassName($pluginName)
{
$pluginNamespace = '\\Tests\\PHPCI\\Plugin\\Util\\Fake\\';
return $pluginNamespace . $pluginName;
}
}

View file

@ -1,8 +1,14 @@
<?php
namespace PHPCI\Plugin\Tests\Util;
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
require_once __DIR__ . "/ExamplePlugins.php";
namespace Tests\PHPCI\Plugin\Util;
use PHPCI\Plugin\Util\Factory;
@ -49,10 +55,9 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
public function testBuildPluginWorksWithConstructorlessPlugins()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace .'ExamplePluginWithNoConstructorArgs';
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$this->assertInstanceOf($expectedPluginClass, $plugin);
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithNoConstructorArgs');
$plugin = $this->testedFactory->buildPlugin($pluginClass);
$this->assertInstanceOf($pluginClass, $plugin);
}
public function testBuildPluginFailsForNonPluginClasses()
@ -63,10 +68,9 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
public function testBuildPluginWorksWithSingleOptionalArgConstructor()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleOptionalArg';
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$this->assertInstanceOf($expectedPluginClass, $plugin);
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithSingleOptionalArg');
$plugin = $this->testedFactory->buildPlugin($pluginClass);
$this->assertInstanceOf($pluginClass, $plugin);
}
public function testBuildPluginThrowsExceptionIfMissingResourcesForRequiredArg()
@ -76,15 +80,13 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
'Unsatisfied dependency: requiredArgument'
);
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleRequiredArg';
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithSingleRequiredArg');
$plugin = $this->testedFactory->buildPlugin($pluginClass);
}
public function testBuildPluginLoadsArgumentsBasedOnName()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleRequiredArg';
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithSingleRequiredArg');
$this->testedFactory->registerResource(
$this->resourceLoader,
@ -92,15 +94,14 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
);
/** @var ExamplePluginWithSingleRequiredArg $plugin */
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$plugin = $this->testedFactory->buildPlugin($pluginClass);
$this->assertEquals($this->expectedResource, $plugin->RequiredArgument);
}
public function testBuildPluginLoadsArgumentsBasedOnType()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleTypedRequiredArg';
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithSingleTypedRequiredArg');
$this->testedFactory->registerResource(
$this->resourceLoader,
@ -109,28 +110,26 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
);
/** @var ExamplePluginWithSingleTypedRequiredArg $plugin */
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$plugin = $this->testedFactory->buildPlugin($pluginClass);
$this->assertEquals($this->expectedResource, $plugin->RequiredArgument);
}
public function testBuildPluginLoadsFullExample()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginFull';
$pluginClass = $this->getFakePluginClassName('ExamplePluginFull');
$this->registerBuildAndBuilder();
/** @var ExamplePluginFull $plugin */
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
$plugin = $this->testedFactory->buildPlugin($pluginClass);
$this->assertInstanceOf($expectedPluginClass, $plugin);
$this->assertInstanceOf($pluginClass, $plugin);
}
public function testBuildPluginLoadsFullExampleWithOptions()
{
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$expectedPluginClass = $namespace . 'ExamplePluginFull';
$pluginClass = $this->getFakePluginClassName('ExamplePluginFull');
$expectedArgs = array(
'thing' => "stuff"
@ -140,7 +139,7 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
/** @var ExamplePluginFull $plugin */
$plugin = $this->testedFactory->buildPlugin(
$expectedPluginClass,
$pluginClass,
$expectedArgs
);
@ -163,10 +162,8 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
realpath(__DIR__ . "/ExamplePluginConfig.php")
);
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
$pluginName = $namespace . 'ExamplePluginWithSingleRequiredArg';
$plugin = $this->testedFactory->buildPlugin($pluginName);
$pluginClass = $this->getFakePluginClassName('ExamplePluginWithSingleRequiredArg');
$plugin = $this->testedFactory->buildPlugin($pluginClass);
// The Example config file defines an array as the resource.
$this->assertEquals(
@ -181,9 +178,11 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
*/
private function registerBuildAndBuilder()
{
$self = $this;
$this->testedFactory->registerResource(
function () {
return $this->getMock(
function () use ($self) {
return $self->getMock(
'PHPCI\Builder',
array(),
array(),
@ -196,8 +195,8 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
);
$this->testedFactory->registerResource(
function () {
return $this->getMock(
function () use ($self) {
return $self->getMock(
'PHPCI\Model\Build',
array(),
array(),
@ -206,8 +205,15 @@ class FactoryTest extends \PHPUnit_Framework_TestCase {
);
},
null,
'PHPCI\\Model\Build'
'PHPCI\\Model\\Build'
);
}
protected function getFakePluginClassName($pluginName)
{
$pluginNamespace = '\\Tests\\PHPCI\\Plugin\\Util\\Fake\\';
return $pluginNamespace . $pluginName;
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* PHPCI - Continuous Integration for PHP
*
* @copyright Copyright 2015, Block 8 Limited.
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md
* @link https://www.phptesting.org/
*/
namespace Tests\PHPCI\Plugin\Util\Fake;
use PHPCI\Builder;
use PHPCI\Model\Build;
use PHPCI\Plugin;
class ExamplePluginFull implements Plugin {
/**
* @var array
*/
public $Options;
public function __construct(
Builder $phpci,
Build $build,
array $options = array()
)
{
$this->Options = $options;
}
public function execute()
{
}
}

Some files were not shown because too many files have changed in this diff Show more