Add Bitbucket webhook

This commit is contained in:
Frederic Guillot 2015-02-08 21:13:59 -05:00
parent 02f7c8d33d
commit 6f94ce6af3
24 changed files with 279 additions and 1 deletions

View File

@ -86,6 +86,7 @@ Documentation
### Integrations
- [Bitbucket webhooks](docs/bitbucket-webhooks.markdown)
- [Github webhooks](docs/github-webhooks.markdown)
- [Gitlab webhooks](docs/gitlab-webhooks.markdown)

View File

@ -4,6 +4,7 @@ namespace Action;
use Integration\GitlabWebhook;
use Integration\GithubWebhook;
use Integration\BitbucketWebhook;
use Model\Task;
/**
@ -28,6 +29,7 @@ class TaskClose extends Base
GithubWebhook::EVENT_ISSUE_CLOSED,
GitlabWebhook::EVENT_COMMIT,
GitlabWebhook::EVENT_ISSUE_CLOSED,
BitbucketWebhook::EVENT_COMMIT,
);
}
@ -44,6 +46,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
case BitbucketWebhook::EVENT_COMMIT:
return array();
default:
return array('column_id' => t('Column'));
@ -63,6 +66,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
case BitbucketWebhook::EVENT_COMMIT:
return array('task_id');
default:
return array('task_id', 'column_id');
@ -95,6 +99,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
case BitbucketWebhook::EVENT_COMMIT:
return true;
default:
return $data['column_id'] == $this->getParam('column_id');

View File

@ -82,4 +82,22 @@ class Webhook extends Base
echo $result ? 'PARSED' : 'IGNORED';
}
/**
* Handle Bitbucket webhooks
*
* @access public
*/
public function bitbucket()
{
if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
$this->response->text('Not Authorized', 401);
}
$this->bitbucketWebhook->setProjectId($this->request->getIntegerParam('project_id'));
$result = $this->bitbucketWebhook->parsePayload(json_decode(@$_POST['payload'], true));
echo $result ? 'PARSED' : 'IGNORED';
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace Integration;
use Event\GenericEvent;
use Event\TaskEvent;
use Model\Task;
/**
* Bitbucket Webhook
*
* @package integration
* @author Frederic Guillot
*/
class BitbucketWebhook extends Base
{
/**
* Events
*
* @var string
*/
const EVENT_COMMIT = 'bitbucket.webhook.commit';
/**
* Project id
*
* @access private
* @var integer
*/
private $project_id = 0;
/**
* Set the project id
*
* @access public
* @param integer $project_id Project id
*/
public function setProjectId($project_id)
{
$this->project_id = $project_id;
}
/**
* Parse events
*
* @access public
* @param array $payload Gitlab event
* @return boolean
*/
public function parsePayload(array $payload)
{
if (! empty($payload['commits'])) {
foreach ($payload['commits'] as $commit) {
if ($this->handleCommit($commit)) {
return true;
}
}
}
return false;
}
/**
* Parse commit
*
* @access public
* @param array $commit Gitlab commit
* @return boolean
*/
public function handleCommit(array $commit)
{
$task_id = $this->task->getTaskIdFromText($commit['message']);
if (! $task_id) {
return false;
}
$task = $this->taskFinder->getById($task_id);
if (! $task) {
return false;
}
if ($task['is_active'] == Task::STATUS_OPEN && $task['project_id'] == $this->project_id) {
$this->container['dispatcher']->dispatch(
self::EVENT_COMMIT,
new TaskEvent(array('task_id' => $task_id) + $task)
);
return true;
}
return false;
}
}

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -680,4 +680,7 @@ return array(
'Disable login form' => 'Désactiver le formulaire d\'authentification',
'Show/hide calendar' => 'Afficher/cacher le calendrier',
'User calendar' => 'Calendrier de l\'utilisateur',
'Bitbucket commit received' => '« Commit » reçu via Bitbucket',
'Bitbucket webhooks' => 'Webhook Bitbucket',
'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
);

View File

@ -4,6 +4,7 @@ namespace Model;
use Integration\GitlabWebhook;
use Integration\GithubWebhook;
use Integration\BitbucketWebhook;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
@ -85,6 +86,7 @@ class Action extends Base
GitlabWebhook::EVENT_COMMIT => t('Gitlab commit received'),
GitlabWebhook::EVENT_ISSUE_OPENED => t('Gitlab issue opened'),
GitlabWebhook::EVENT_ISSUE_CLOSED => t('Gitlab issue closed'),
BitbucketWebhook::EVENT_COMMIT => t('Bitbucket commit received'),
);
asort($values);

View File

@ -62,6 +62,7 @@ class ClassProvider implements ServiceProviderInterface
'Integration' => array(
'GitlabWebhook',
'GithubWebhook',
'BitbucketWebhook',
)
);

View File

@ -12,4 +12,10 @@
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'gitlab', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
<p class="form-help"><a href="http://kanboard.net/documentation/gitlab-webhooks" target="_blank"><?= t('Help on Gitlab webhooks') ?></a></p>
</div>
<h3><i class="fa fa-bitbucket fa-fw"></i>&nbsp;<?= t('Bitbucket webhooks') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'bitbucket', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
<p class="form-help"><a href="http://kanboard.net/documentation/bitbucket-webhooks" target="_blank"><?= t('Help on Bitbucket webhooks') ?></a></p>
</div>

View File

@ -0,0 +1,40 @@
Bitbucket webhooks
==================
Bitbucket events can be connected to Kanboard automatic actions.
List of supported events
------------------------
- Bitbucket commit received
List of supported actions
-------------------------
- Close a task
Configuration
-------------
![Bitbucket configuration](http://kanboard.net/screenshots/documentation/bitbucket-webhooks.png)
1. On Kanboard, go to the project settings and choose the section **Integrations**
2. Copy the Bitbucket webhook url
3. On Bitbucket, go to the project settings and go to the section **Hooks**
4. Select the service **POST**
5. Paste the url and save
Examples
--------
### Close a Kanboard task when a commit pushed to Bitbucket
- Choose the event: **Bitbucket commit received**
- Choose the action: **Close the task**
When one or more commits are sent to Bitbucket, Kanboard will receive the information, each commit message with a task number included will be closed.
Example:
- Commit message: "Fix bug #1234"
- That will close the Kanboard task #1234

View File

@ -23,7 +23,7 @@ Configuration
1. On Kanboard, go to the project settings and choose the section **Integrations**
2. Copy the Gitlab webhook url
3. On Gitlab, go to the project settings and go the section **Webhooks**
3. On Gitlab, go to the project settings and go to the section **Webhooks**
4. Check the boxes **Push Events** and **Issues Events**
5. Paste the url and save

View File

@ -0,0 +1,65 @@
<?php
require_once __DIR__.'/Base.php';
use Integration\BitbucketWebhook;
use Model\TaskCreation;
use Model\TaskFinder;
use Model\Project;
class BitbucketWebhookTest extends Base
{
private $post_payload = '{"repository": {"website": "", "fork": false, "name": "webhooks", "scm": "git", "owner": "minicoders", "absolute_url": "/minicoders/webhooks/", "slug": "webhooks", "is_private": true}, "truncated": false, "commits": [{"node": "28569937627f", "files": [{"type": "added", "file": "README.md"}], "raw_author": "Frederic Guillot <fred@localhost>", "utctimestamp": "2015-02-09 00:57:45+00:00", "author": "Frederic Guillot", "timestamp": "2015-02-09 01:57:45", "raw_node": "28569937627fb406eeda9376a02b39581a974d4f", "parents": [], "branch": "master", "message": "first commit\\n", "revision": null, "size": -1}, {"node": "285699376274", "files": [{"type": "added", "file": "README.md"}], "raw_author": "Frederic Guillot <fred@localhost>", "utctimestamp": "2015-02-09 00:57:45+00:00", "author": "Frederic Guillot", "timestamp": "2015-02-09 01:57:45", "raw_node": "28569937627fb406eeda9376a02b39581a974d4f", "parents": [], "branch": "master", "message": "Fix #2\\n", "revision": null, "size": -1}], "canon_url": "https://bitbucket.org", "user": "minicoders"}';
public function testHandleCommit()
{
$g = new BitbucketWebhook($this->container);
$p = new Project($this->container);
$tc = new TaskCreation($this->container);
$tf = new TaskFinder($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$g->setProjectId(1);
$this->container['dispatcher']->addListener(BitbucketWebhook::EVENT_COMMIT, function() {});
$event = json_decode($this->post_payload, true);
// No task
$this->assertFalse($g->handleCommit($event['commits'][0]));
// Create task with the wrong id
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertFalse($g->handleCommit($event['commits'][1]));
// Create task with the right id
$this->assertEquals(2, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertTrue($g->handleCommit($event['commits'][1]));
$called = $this->container['dispatcher']->getCalledListeners();
$this->assertArrayHasKey(BitbucketWebhook::EVENT_COMMIT.'.closure', $called);
}
public function testParsePayload()
{
$g = new BitbucketWebhook($this->container);
$p = new Project($this->container);
$tc = new TaskCreation($this->container);
$tf = new TaskFinder($this->container);
$this->container['dispatcher']->addListener(BitbucketWebhook::EVENT_COMMIT, function() {});
$this->assertEquals(1, $p->create(array('name' => 'test')));
$g->setProjectId(1);
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(2, $tc->create(array('title' => 'test', 'project_id' => 1)));
$event = json_decode($this->post_payload, true);
$this->assertTrue($g->parsePayload($event));
$called = $this->container['dispatcher']->getCalledListeners();
$this->assertArrayHasKey(BitbucketWebhook::EVENT_COMMIT.'.closure', $called);
}
}