Merge branch 'feature-github-webhook'

This commit is contained in:
Dmitry Khomutov 2017-04-23 00:06:01 +07:00
commit 1d8da6bfc0
No known key found for this signature in database
GPG key ID: 7EB36C9576F9ECB9
15 changed files with 280 additions and 82 deletions

View file

@ -57,9 +57,9 @@ class CreateBuildCommand extends Command
*/
public function execute(InputInterface $input, OutputInterface $output)
{
$projectId = $input->getArgument('projectId');
$commitId = $input->getOption('commit');
$branch = $input->getOption('branch');
$projectId = $input->getArgument('projectId');
$commitId = $input->getOption('commit');
$branch = $input->getOption('branch');
$environment = $input->hasOption('environment') ? $input->getOption('environment') : null;
$project = $this->projectStore->getById($projectId);

View file

@ -94,10 +94,12 @@ class ProjectController extends PHPCensor\Controller
$this->view->perPage = $perPage;
$this->layout->title = $project->getTitle();
$this->layout->subtitle = '';
if (!empty($this->view->environment)) {
$this->layout->subtitle = $this->view->environment;
} else {
$this->layout->subtitle = $this->view->branch;
$this->layout->subtitle = '<i class="fa fa-gear"></i> ' . $this->view->environment;
} elseif (!empty($this->view->branch)) {
$this->layout->subtitle = '<i class="fa fa-code-fork"></i> ' . $this->view->branch;
}
return $this->view->render();
@ -148,7 +150,16 @@ class ProjectController extends PHPCensor\Controller
}
$email = $_SESSION['php-censor-user']->getEmail();
$build = $this->buildService->createBuild($project, $environment, null, urldecode($branch), $email, null, $extra);
$build = $this->buildService->createBuild(
$project,
$environment,
null,
urldecode($branch),
null,
$email,
null,
$extra
);
if ($this->buildService->queueError) {
$_SESSION['global_error'] = Lang::get('add_to_queue_failed');

View file

@ -121,6 +121,7 @@ class WebhookController extends Controller
$project,
$commit['new']['target']['hash'],
$commit['new']['name'],
null,
$email,
$commit['new']['target']['message']
);
@ -152,6 +153,7 @@ class WebhookController extends Controller
$project,
$commit['raw_node'],
$commit['branch'],
null,
$email,
$commit['message']
);
@ -179,7 +181,7 @@ class WebhookController extends Controller
$commitMessage = $this->getParam('message');
$committer = $this->getParam('committer');
return $this->createBuild($project, $commit, $branch, $committer, $commitMessage);
return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage);
}
/**
@ -197,16 +199,20 @@ class WebhookController extends Controller
$payload = json_decode($this->getParam('payload'), true);
break;
default:
return ['status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401];
return [
'status' => 'failed',
'error' => 'Content type not supported.',
'responseCode' => 401
];
}
// Handle Pull Request web hooks:
// Handle Pull Request webhooks:
if (array_key_exists('pull_request', $payload)) {
return $this->githubPullRequest($project, $payload);
}
// Handle Push web hooks:
if (array_key_exists('commits', $payload)) {
// Handle Push (and Tag) webhooks:
if (array_key_exists('head_commit', $payload)) {
return $this->githubCommitRequest($project, $payload);
}
@ -223,47 +229,50 @@ class WebhookController extends Controller
*/
protected function githubCommitRequest(Project $project, array $payload)
{
// Github sends a payload when you close a pull request with a
// non-existent commit. We don't want this.
if (array_key_exists('after', $payload) && $payload['after'] === '0000000000000000000000000000000000000000') {
// Github sends a payload when you close a pull request with a non-existent commit. We don't want this.
if (
array_key_exists('after', $payload) &&
$payload['after'] === '0000000000000000000000000000000000000000'
) {
return ['status' => 'ignored'];
}
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['head_commit']) && $payload['head_commit']) {
$isTag = (substr($payload['ref'], 0, 10) == 'refs/tags/') ? true : false;
$commit = $payload['head_commit'];
$results = [];
$status = 'failed';
foreach ($payload['commits'] as $commit) {
if (!$commit['distinct']) {
$results[$commit['id']] = ['status' => 'ignored'];
continue;
}
if (!$commit['distinct']) {
$results[$commit['id']] = ['status' => 'ignored'];
} else {
try {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['committer']['email'];
$tag = null;
if ($isTag) {
$tag = str_replace('refs/tags/', '', $payload['ref']);
$branch = str_replace('refs/heads/', '', $payload['base_ref']);
$committer = $payload['pusher']['email'];
} else {
$branch = str_replace('refs/heads/', '', $payload['ref']);
$committer = $commit['committer']['email'];
}
$results[$commit['id']] = $this->createBuild(
$project,
$commit['id'],
$branch,
$tag,
$committer,
$commit['message']
);
$status = 'ok';
} catch (Exception $ex) {
$results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()];
}
}
return ['status' => $status, 'commits' => $results];
}
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 ['status' => $status, 'commits' => $results];
}
return ['status' => 'ignored', 'message' => 'Unusable payload.'];
@ -335,7 +344,7 @@ class WebhookController extends Controller
'remote_url' => $payload['pull_request']['head']['repo'][$remoteUrlKey],
];
$results[$id] = $this->createBuild($project, $id, $branch, $committer, $message, $extra);
$results[$id] = $this->createBuild($project, $id, $branch, null, $committer, $message, $extra);
$status = 'ok';
} catch (Exception $ex) {
$results[$id] = ['status' => 'failed', 'error' => $ex->getMessage()];
@ -363,7 +372,7 @@ class WebhookController extends Controller
$commit = $attributes['last_commit'];
$committer = $commit['author']['email'];
return $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']);
return $this->createBuild($project, $commit['id'], $branch, null, $committer, $commit['message']);
}
}
@ -381,6 +390,7 @@ class WebhookController extends Controller
$project,
$commit['id'],
$branch,
null,
$committer,
$commit['message']
);
@ -413,7 +423,7 @@ class WebhookController extends Controller
$commitMessage = $this->getParam('message');
$committer = $this->getParam('committer');
return $this->createBuild($project, $commit, $branch, $committer, $commitMessage);
return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage);
}
/**
@ -467,6 +477,7 @@ class WebhookController extends Controller
$project,
$commit['id'],
$branch,
null,
$committer,
$commit['message']
);
@ -486,11 +497,12 @@ class WebhookController extends Controller
* Wrapper for creating a new build.
*
* @param Project $project
* @param string $commitId
* @param string $branch
* @param string $committer
* @param string $commitMessage
* @param array $extra
* @param string $commitId
* @param string $branch
* @param string $tag
* @param string $committer
* @param string $commitMessage
* @param array $extra
*
* @return array
*
@ -500,6 +512,7 @@ class WebhookController extends Controller
Project $project,
$commitId,
$branch,
$tag,
$committer,
$commitMessage,
array $extra = null
@ -512,28 +525,43 @@ class WebhookController extends Controller
$builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId);
$ignore_environments = [];
$ignore_tags = [];
if ($builds['count']) {
foreach($builds['items'] as $build) {
/** @var Build $build */
$ignore_environments[$build->getId()] = $build->getEnvironment();
$ignore_tags[$build->getId()] = $build->getTag();
}
}
$environments = $project->getEnvironmentsObjects();
if ($environments['count']) {
$created_builds = [];
$created_builds = [];
$environment_names = $project->getEnvironmentsNamesByBranch($branch);
// use base branch from project
if (!empty($environment_names)) {
$duplicates = [];
foreach ($environment_names as $environment_name) {
if (!in_array($environment_name, $ignore_environments)) {
if (
!in_array($environment_name, $ignore_environments) ||
($tag && !in_array($tag, $ignore_tags, true))
) {
// If not, create a new build job for it:
$build = $this->buildService->createBuild($project, $environment_name, $commitId, $project->getBranch(), $committer, $commitMessage, $extra);
$created_builds[] = array(
'id' => $build->getID(),
'environment' => $environment_name,
$build = $this->buildService->createBuild(
$project,
$environment_name,
$commitId,
$project->getBranch(),
$tag,
$committer,
$commitMessage,
$extra
);
$created_builds[] = [
'id' => $build->getID(),
'environment' => $environment_name,
];
} else {
$duplicates[] = array_search($environment_name, $ignore_environments);
}
@ -552,8 +580,21 @@ class WebhookController extends Controller
}
} else {
$environment_name = null;
if (!in_array($environment_name, $ignore_environments)) {
$build = $this->buildService->createBuild($project, null, $commitId, $branch, $committer, $commitMessage, $extra);
if (
!in_array($environment_name, $ignore_environments, true) ||
($tag && !in_array($tag, $ignore_tags, true))
) {
$build = $this->buildService->createBuild(
$project,
null,
$commitId,
$branch,
$tag,
$committer,
$commitMessage,
$extra
);
return ['status' => 'ok', 'buildID' => $build->getID()];
} else {
return [

View file

@ -0,0 +1,24 @@
<?php
use Phinx\Migration\AbstractMigration;
class AddedTagColumnToBuildTable extends AbstractMigration
{
public function up()
{
$table = $this->table('build');
if (!$table->hasColumn('tag')) {
$table->addColumn('tag', 'string', ['limit' => 250, 'null' => true])->save();
}
}
public function down()
{
$table = $this->table('build');
if ($table->hasColumn('tag')) {
$table->removeColumn('tag')->save();
}
}
}

View file

@ -51,6 +51,7 @@ class Build extends Model
'status' => null,
'log' => null,
'branch' => null,
'tag' => null,
'created' => null,
'started' => null,
'finished' => null,
@ -71,6 +72,7 @@ class Build extends Model
'status' => 'getStatus',
'log' => 'getLog',
'branch' => 'getBranch',
'tag' => 'getTag',
'created' => 'getCreated',
'started' => 'getStarted',
'finished' => 'getFinished',
@ -94,6 +96,7 @@ class Build extends Model
'status' => 'setStatus',
'log' => 'setLog',
'branch' => 'setBranch',
'setTag' => 'setTag',
'created' => 'setCreated',
'started' => 'setStarted',
'finished' => 'setFinished',
@ -142,6 +145,11 @@ class Build extends Model
'length' => 250,
'default' => 'master',
],
'tag' => [
'type' => 'varchar',
'length' => 250,
'default' => null,
],
'created' => [
'type' => 'datetime',
'nullable' => true,
@ -712,6 +720,14 @@ class Build extends Model
return '#';
}
/**
* Get link to tag from another source (i.e. Github)
*/
public function getTagLink()
{
return '#';
}
/**
* Return a template to use to generate a link to a specific file.
*
@ -986,6 +1002,36 @@ class Build extends Model
return false;
}
/**
* Get the value of Tag / tag.
*
* @return string
*/
public function getTag()
{
$rtn = $this->data['tag'];
return $rtn;
}
/**
* Set the value of Tag / tag.
*
* @param $value string
*/
public function setTag($value)
{
$this->validateString('Tag', $value);
if ($this->data['tag'] === $value) {
return;
}
$this->data['tag'] = $value;
$this->setModified('tag');
}
/**
* Get the value of Environment / environment.
*

View file

@ -32,6 +32,14 @@ class GithubBuild extends RemoteGitBuild
return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch();
}
/**
* Get link to tag from another source (i.e. Github)
*/
public function getTagLink()
{
return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getTag();
}
/**
* Send status updates to any relevant third parties (i.e. Github)
*/

View file

@ -38,6 +38,7 @@ class BuildService
* @param string $environment
* @param string|null $commitId
* @param string|null $branch
* @param string|null $tag
* @param string|null $committerEmail
* @param string|null $commitMessage
* @param string|null $extra
@ -49,6 +50,7 @@ class BuildService
$environment,
$commitId = null,
$branch = null,
$tag = null,
$committerEmail = null,
$commitMessage = null,
$extra = null
@ -75,6 +77,10 @@ class BuildService
$build->setBranch($project->getBranch());
}
if (!is_null($tag)) {
$build->setTag($tag);
}
if (!is_null($committerEmail)) {
$build->setCommitterEmail($committerEmail);
}

View file

@ -177,14 +177,17 @@ class BuildStore extends Store
/**
* Return an array of builds for a given project and commit ID.
* @param $projectId
* @param $commitId
*
* @param integer $projectId
* @param string $commitId
*
* @return array
*/
public function getByProjectAndCommit($projectId, $commitId)
{
$query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :project_id AND {{commit_id}} = :commit_id';
$stmt = Database::getConnection('read')->prepareCommon($query);
$stmt = Database::getConnection('read')->prepareCommon($query);
$stmt->bindValue(':project_id', $projectId);
$stmt->bindValue(':commit_id', $commitId);

View file

@ -24,9 +24,14 @@
<tr>
<th><?php Lang::out('branch'); ?></th>
<td style="text-align: right">
<a target="_blank" href="<?php print $build->getBranchLink(); ?>">
<span class="label label-default"><?php print $build->getBranch(); ?></span>
<a target="_blank" href="<?= $build->getBranchLink(); ?>">
<i class="fa fa-code-fork"></i> <?= $build->getBranch(); ?>
</a>
<?php if ($tag = $build->getTag()): ?> /
<a target="_blank" href="<?= $build->getTagLink(); ?>">
<i class="fa fa-tag"></i> <?= $tag; ?>
</a>
<?php endif; ?>
</td>
</tr>
@ -35,7 +40,7 @@
<td style="text-align: right">
<?php
$environment = $build->getEnvironment();
echo !empty($environment) ? ('<span class="label label-default">' . $environment . '</span>') : '—' ;
echo !empty($environment) ? ('<i class="fa fa-gear"> ' . $environment) : '—' ;
?>
</td>
</tr>
@ -43,12 +48,14 @@
<tr>
<th><?php Lang::out('merged_branches'); ?></th>
<td style="text-align: right">
<span class="label label-default"><?php print $build->getBranch() ?></span>
<a target="_blank" href="<?= $build->getBranchLink(); ?>">
<i class="fa fa-code-fork"></i> <?= $build->getBranch(); ?>
</a>
+ <?php
$branches = $build->getExtra('branches');
if (!empty($branches)) {
foreach($branches as $branch) {
?><span class="label label-default"><?php print $branch ?></span><?php
?><i class="fa fa-code-fork"></i> <?php print $branch ?><?php
}
} else {
?>&mdash;<?php

View file

@ -98,6 +98,13 @@ if ($buildCount > 0) {
<i class="fa fa-<?php print $project->getIcon(); ?>"></i>
</div>
<a href="<?php print APP_URL; ?>project/view/<?php print $project->getId(); ?>" class="small-box-footer small-box-footer-project">
<div class="pull-left" style="margin-left: 10px">
<?php if ($project->getAllowPublicStatus()): ?>
<i class="fa fa-unlock"></i>
<?php else: ?>
<i class="fa fa-lock"></i>
<?php endif; ?>
</div>
<?php Lang::out('view_project'); ?> (<?php print $counts; ?>) <i class="fa fa-arrow-circle-right"></i>
</a>

View file

@ -57,11 +57,11 @@ use PHPCensor\Model\Build;
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i>
<?php
echo $updated->format('H:i:s');
if ($build->getStatus() != Build::STATUS_PENDING) {
echo ' (' . $build->getPrettyDuration() . ')';
}
<?php
echo $updated->format('H:i:s');
if ($build->getStatus() != Build::STATUS_PENDING) {
echo ' &mdash; ' . $build->getDuration(); ?> <?= Lang::get('seconds');
}
?>
</span>
<h3 class="timeline-header">
@ -69,17 +69,23 @@ use PHPCensor\Model\Build;
<?php print $build->getProject()->getTitle(); ?>
</a>
<span><?php print $environment; ?></span>
-
&mdash;
<a href="<?php print APP_URL; ?>build/view/<?php print $build->getId(); ?>">
Build #<?php print $build->getId(); ?>
</a>
-
&mdash;
<?php print $label; ?>
</h3>
<div class="timeline-body">
<a href="<?php echo $build->getBranchLink();?>"><?php echo $build->getBranch(); ?></a>
<?php print $branches ? ' + '.implode(', ', $branches) : ''; ?> -
<a href="<?= $build->getBranchLink();?>"><i class="fa fa-code-fork"></i> <?php echo $build->getBranch(); ?></a>
<?= $branches ? ' + '.implode(', ', $branches) : ''; ?>
<?php if ($tag = $build->getTag()): ?> /
<a href="<?= $build->getTagLink(); ?>" target="_blank">
<i class="fa fa-tag"></i> <?= $tag; ?>
</a>
<?php endif; ?>
&mdash;
<?php
if ($build->getCommitId() !== 'Manual') {
print sprintf(
@ -92,7 +98,8 @@ use PHPCensor\Model\Build;
print Lang::get('manual_build');
}
?>
- <?php print $build->getCommitMessage(); ?>
&mdash;
<?php print $build->getCommitMessage(); ?>
</div>
</div>
</li>

View file

@ -99,6 +99,13 @@ foreach($projects as $project):
<i class="fa fa-<?php print $project->getIcon(); ?>"></i>
</div>
<a href="<?php print APP_URL; ?>project/view/<?php print $project->getId(); ?>" class="small-box-footer small-box-footer-project">
<div class="pull-left" style="margin-left: 10px">
<?php if ($project->getAllowPublicStatus()): ?>
<i class="fa fa-unlock"></i>
<?php else: ?>
<i class="fa fa-lock"></i>
<?php endif; ?>
</div>
<?php Lang::out('view_project'); ?> (<?php print $counts[$project->getId()]; ?>) <i class="fa fa-arrow-circle-right"></i>
</a>

View file

@ -89,10 +89,10 @@ use PHPCensor\Model\Build;
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i>
<?php
echo $updated->format('H:i:s');
if ($build->getStatus() != Build::STATUS_PENDING) {
echo ' (' . $build->getPrettyDuration() . ')';
}
echo $updated->format('H:i:s');
if ($build->getStatus() != Build::STATUS_PENDING) {
echo ' &mdash; ' . $build->getDuration(); ?> <?= Lang::get('seconds');
}
?>
</span>
<h3 class="timeline-header">
@ -100,17 +100,23 @@ use PHPCensor\Model\Build;
<?php print $build->getProject()->getTitle(); ?>
</a>
<span><?php print $environment; ?></span>
-
&mdash;
<a href="<?php print APP_URL; ?>build/view/<?php print $build->getId(); ?>">
Build #<?php print $build->getId(); ?>
</a>
-
&mdash;
<?php print $label; ?>
</h3>
<div class="timeline-body">
<a href="<?php echo $build->getBranchLink();?>"><?php echo $build->getBranch(); ?></a>
<?php print $branches ? ' + '.implode(', ', $branches) : ''; ?> -
<a href="<?= $build->getBranchLink();?>"><i class="fa fa-code-fork"></i> <?php echo $build->getBranch(); ?></a>
<?= $branches ? ' + '.implode(', ', $branches) : ''; ?>
<?php if ($tag = $build->getTag()): ?> /
<a href="<?= $build->getTagLink(); ?>" target="_blank">
<i class="fa fa-tag"></i> <?= $tag; ?>
</a>
<?php endif; ?>
&mdash;
<?php
if ($build->getCommitId() !== 'Manual') {
print sprintf(
@ -123,7 +129,8 @@ use PHPCensor\Model\Build;
print Lang::get('manual_build');
}
?>
- <?php print $build->getCommitMessage(); ?>
&mdash;
<?php print $build->getCommitMessage(); ?>
</div>
</div>
</li>

View file

@ -58,8 +58,15 @@ $branches = $build->getExtra('branches');
?>
</td>
<td>
<a href="<?php print $build->getBranchLink(); ?>" target="_blank"><?php print $build->getBranch(); ?></a>
<?php print $branches ? ' + '.implode(', ', $branches) : ''; ?>
<a href="<?= $build->getBranchLink(); ?>" target="_blank">
<i class="fa fa-code-fork"></i> <?= $build->getBranch(); ?>
</a>
<?= $branches ? ' + '.implode(', ', $branches) : ''; ?>
<?php if ($tag = $build->getTag()): ?> /
<a href="<?= $build->getTagLink(); ?>" target="_blank">
<i class="fa fa-tag"></i> <?= $tag; ?>
</a>
<?php endif; ?>
</td>
<td>
<?php
@ -68,7 +75,7 @@ $branches = $build->getExtra('branches');
?>
</td>
<td>
<?php print $build->getDuration(); ?> <?= Lang::get('seconds'); ?>
<?= $build->getDuration(); ?> <?= Lang::get('seconds'); ?>
</td>
<td>
<div class="btn-group btn-group-right">

View file

@ -87,7 +87,15 @@ class BuildServiceTest extends \PHPUnit_Framework_TestCase
$project->setType('hg');
$project->setId(101);
$returnValue = $this->testedService->createBuild($project, null, '123', 'testbranch', 'test@example.com', 'test');
$returnValue = $this->testedService->createBuild(
$project,
null,
'123',
'testbranch',
null,
'test@example.com',
'test'
);
$this->assertEquals('testbranch', $returnValue->getBranch());
$this->assertEquals('123', $returnValue->getCommitId());
@ -109,7 +117,16 @@ class BuildServiceTest extends \PHPUnit_Framework_TestCase
$project->setType('bitbucket');
$project->setId(101);
$returnValue = $this->testedService->createBuild($project, null, null, null, null, null, ['item1' => 1001]);
$returnValue = $this->testedService->createBuild(
$project,
null,
null,
null,
null,
null,
null,
['item1' => 1001]
);
$this->assertEquals(1001, $returnValue->getExtra('item1'));
}