summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.markdown4
-rw-r--r--app/Action/Base.php1
-rw-r--r--app/Action/CommentCreation.php83
-rw-r--r--app/Controller/Project.php1
-rw-r--r--app/Controller/Webhook.php4
-rw-r--r--app/Locale/da_DK/translations.php5
-rw-r--r--app/Locale/de_DE/translations.php5
-rw-r--r--app/Locale/es_ES/translations.php5
-rw-r--r--app/Locale/fi_FI/translations.php5
-rw-r--r--app/Locale/fr_FR/translations.php5
-rw-r--r--app/Locale/it_IT/translations.php5
-rw-r--r--app/Locale/ja_JP/translations.php5
-rw-r--r--app/Locale/pl_PL/translations.php5
-rw-r--r--app/Locale/pt_BR/translations.php5
-rw-r--r--app/Locale/ru_RU/translations.php5
-rw-r--r--app/Locale/sv_SE/translations.php5
-rw-r--r--app/Locale/th_TH/translations.php5
-rw-r--r--app/Locale/zh_CN/translations.php5
-rw-r--r--app/Model/Action.php2
-rw-r--r--app/Model/GithubWebhook.php87
-rw-r--r--app/Template/project_show.php11
-rw-r--r--docs/automatic-actions.markdown11
-rw-r--r--docs/github-webhooks.markdown94
23 files changed, 348 insertions, 15 deletions
diff --git a/README.markdown b/README.markdown
index e235eb80..c8650a9c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -98,6 +98,10 @@ Documentation
- [GitHub authentication](docs/github-authentication.markdown)
- [Reverse proxy authentication](docs/reverse-proxy-authentication.markdown)
+### Integration
+
+- [Github webhooks](docs/github-webhooks.markdown)
+
#### Developers and sysadmins
- [Board configuration](docs/board-configuration.markdown)
diff --git a/app/Action/Base.php b/app/Action/Base.php
index 80930a4c..be9c3d48 100644
--- a/app/Action/Base.php
+++ b/app/Action/Base.php
@@ -13,6 +13,7 @@ use Core\Tool;
* @author Frederic Guillot
*
* @property \Model\Acl $acl
+ * @property \Model\Comment $comment
* @property \Model\Task $task
* @property \Model\TaskFinder $taskFinder
*/
diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php
new file mode 100644
index 00000000..3543403d
--- /dev/null
+++ b/app/Action/CommentCreation.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Action;
+
+use Model\GithubWebhook;
+
+/**
+ * Create automatically a comment from a webhook
+ *
+ * @package action
+ * @author Frederic Guillot
+ */
+class CommentCreation extends Base
+{
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ GithubWebhook::EVENT_ISSUE_COMMENT,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array();
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return array
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'reference',
+ 'comment',
+ 'user_id',
+ 'task_id',
+ );
+ }
+
+ /**
+ * Execute the action (create a new comment)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ return $this->comment->create(array(
+ 'reference' => $data['reference'],
+ 'comment' => $data['comment'],
+ 'task_id' => $data['task_id'],
+ 'user_id' => $data['user_id'],
+ ));
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return true;
+ }
+}
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index d749ef53..c5f16496 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -54,6 +54,7 @@ class Project extends Base
$this->response->html($this->projectLayout('project_show', array(
'project' => $project,
'stats' => $this->project->getStats($project['id']),
+ 'webhook_token' => $this->config->get('webhook_token'),
'title' => $project['name'],
)));
}
diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php
index 71acab08..fa9c5834 100644
--- a/app/Controller/Webhook.php
+++ b/app/Controller/Webhook.php
@@ -55,9 +55,11 @@ class Webhook extends Base
$this->githubWebhook->setProjectId($this->request->getIntegerParam('project_id'));
- $this->githubWebhook->parsePayload(
+ $result = $this->githubWebhook->parsePayload(
$this->request->getHeader('X-Github-Event'),
$this->request->getBody()
);
+
+ echo $result ? 'PARSED' : 'IGNORED';
}
}
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index f3523aa4..5c8fb07f 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index f9124641..eb6c8607 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index a18597fc..479983e7 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index e5303adc..78a7c84e 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index 8b1287ce..5f7e7935 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -553,4 +553,9 @@ return array(
'Everybody have access to this project.' => 'Tout le monde a acccès à ce projet.',
'Webhooks' => 'Webhooks',
'API' => 'API',
+ 'Integration' => 'Intégration',
+ 'Github webhook' => 'Webhook Github',
+ 'Help on Github webhook' => 'Aide sur les webhooks Github',
+ 'Create a comment from an external provider' => 'Créer un commentaire depuis un fournisseur externe',
+ 'Github issue comment created' => 'Commentaire créé sur un ticket Github',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 2ec81f21..e0b2b895 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index e05c13e3..440c1e8a 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index b3c6acf1..a294de75 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 902943cc..6986e785 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index a1c79de3..ae752520 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index dbd21401..d3a1c5ed 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 6c6a41bf..65ea09ba 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -553,4 +553,9 @@ return array(
'Everybody have access to this project.' => 'ทุกคนสามารถเข้าถึงโปรเจคนี้',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index bfe15067..11f45f7d 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -553,4 +553,9 @@ return array(
// 'Everybody have access to this project.' => '',
// 'Webhooks' => '',
// 'API' => '',
+ // 'Integration' => '',
+ // 'Github webhook' => '',
+ // 'Help on Github webhook' => '',
+ // 'Create a comment from an external provider' => '',
+ // 'Github issue comment created' => '',
);
diff --git a/app/Model/Action.php b/app/Model/Action.php
index 56a1a2bb..c3acdc5b 100644
--- a/app/Model/Action.php
+++ b/app/Model/Action.php
@@ -46,6 +46,7 @@ class Action extends Base
'TaskAssignColorUser' => t('Assign a color to a specific user'),
'TaskAssignColorCategory' => t('Assign automatically a color based on a category'),
'TaskAssignCategoryColor' => t('Assign automatically a category based on a color'),
+ 'CommentCreation' => t('Create a comment from an external provider'),
'TaskCreation' => t('Create a task from an external provider'),
'TaskAssignUser' => t('Change the assignee based on an external username'),
'TaskAssignCategoryLabel' => t('Change the category based on an external label'),
@@ -78,6 +79,7 @@ class Action extends Base
GithubWebhook::EVENT_ISSUE_REOPENED => t('Github issue reopened'),
GithubWebhook::EVENT_ISSUE_ASSIGNEE_CHANGE => t('Github issue assignee change'),
GithubWebhook::EVENT_ISSUE_LABEL_CHANGE => t('Github issue label change'),
+ GithubWebhook::EVENT_ISSUE_COMMENT => t('Github issue comment created'),
);
asort($values);
diff --git a/app/Model/GithubWebhook.php b/app/Model/GithubWebhook.php
index 6624a782..58c2fa54 100644
--- a/app/Model/GithubWebhook.php
+++ b/app/Model/GithubWebhook.php
@@ -48,6 +48,7 @@ class GithubWebhook extends Base
* @access public
* @param string $type Github event type
* @param string $payload Raw Github event (JSON)
+ * @return boolean
*/
public function parsePayload($type, $payload)
{
@@ -58,7 +59,11 @@ class GithubWebhook extends Base
return $this->parsePushEvent($payload);
case 'issues':
return $this->parseIssueEvent($payload);
+ case 'issue_comment':
+ return $this->parseCommentIssueEvent($payload);
}
+
+ return false;
}
/**
@@ -66,6 +71,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $payload Event data
+ * @return boolean
*/
public function parsePushEvent(array $payload)
{
@@ -87,6 +93,8 @@ class GithubWebhook extends Base
$this->event->trigger(self::EVENT_COMMIT, array('task_id' => $task_id) + $task);
}
}
+
+ return true;
}
/**
@@ -94,32 +102,57 @@ class GithubWebhook extends Base
*
* @access public
* @param array $payload Event data
+ * @return boolean
*/
public function parseIssueEvent(array $payload)
{
switch ($payload['action']) {
case 'opened':
- $this->handleIssueOpened($payload['issue']);
- break;
+ return $this->handleIssueOpened($payload['issue']);
case 'closed':
- $this->handleIssueClosed($payload['issue']);
- break;
+ return $this->handleIssueClosed($payload['issue']);
case 'reopened':
- $this->handleIssueReopened($payload['issue']);
- break;
+ return $this->handleIssueReopened($payload['issue']);
case 'assigned':
- $this->handleIssueAssigned($payload['issue']);
- break;
+ return $this->handleIssueAssigned($payload['issue']);
case 'unassigned':
- $this->handleIssueUnassigned($payload['issue']);
- break;
+ return $this->handleIssueUnassigned($payload['issue']);
case 'labeled':
- $this->handleIssueLabeled($payload['issue'], $payload['label']);
- break;
+ return $this->handleIssueLabeled($payload['issue'], $payload['label']);
case 'unlabeled':
- $this->handleIssueUnlabeled($payload['issue'], $payload['label']);
- break;
+ return $this->handleIssueUnlabeled($payload['issue'], $payload['label']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse comment issue events
+ *
+ * @access public
+ * @param array $payload Event data
+ * @return boolean
+ */
+ public function parseCommentIssueEvent(array $payload)
+ {
+ $task = $this->taskFinder->getByReference($payload['issue']['number']);
+ $user = $this->user->getByUsername($payload['comment']['user']['login']);
+
+ if ($task && $user) {
+
+ $event = array(
+ 'project_id' => $this->project_id,
+ 'reference' => $payload['comment']['id'],
+ 'comment' => $payload['comment']['body'],
+ 'user_id' => $user['id'],
+ 'task_id' => $task['id'],
+ );
+
+ $this->event->trigger(self::EVENT_ISSUE_COMMENT, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -127,6 +160,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $issue Issue data
+ * @return boolean
*/
public function handleIssueOpened(array $issue)
{
@@ -138,6 +172,7 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_OPENED, $event);
+ return true;
}
/**
@@ -145,6 +180,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $issue Issue data
+ * @return boolean
*/
public function handleIssueClosed(array $issue)
{
@@ -158,7 +194,10 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_CLOSED, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -166,6 +205,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $issue Issue data
+ * @return boolean
*/
public function handleIssueReopened(array $issue)
{
@@ -179,7 +219,10 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_REOPENED, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -187,6 +230,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $issue Issue data
+ * @return boolean
*/
public function handleIssueAssigned(array $issue)
{
@@ -203,7 +247,10 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_ASSIGNEE_CHANGE, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -211,6 +258,7 @@ class GithubWebhook extends Base
*
* @access public
* @param array $issue Issue data
+ * @return boolean
*/
public function handleIssueUnassigned(array $issue)
{
@@ -226,7 +274,10 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_ASSIGNEE_CHANGE, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -235,6 +286,7 @@ class GithubWebhook extends Base
* @access public
* @param array $issue Issue data
* @param array $label Label data
+ * @return boolean
*/
public function handleIssueLabeled(array $issue, array $label)
{
@@ -250,7 +302,10 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_LABEL_CHANGE, $event);
+ return true;
}
+
+ return false;
}
/**
@@ -259,6 +314,7 @@ class GithubWebhook extends Base
* @access public
* @param array $issue Issue data
* @param array $label Label data
+ * @return boolean
*/
public function handleIssueUnlabeled(array $issue, array $label)
{
@@ -275,6 +331,9 @@ class GithubWebhook extends Base
);
$this->event->trigger(self::EVENT_ISSUE_LABEL_CHANGE, $event);
+ return true;
}
+
+ return false;
}
}
diff --git a/app/Template/project_show.php b/app/Template/project_show.php
index facdc60a..907d1b70 100644
--- a/app/Template/project_show.php
+++ b/app/Template/project_show.php
@@ -53,3 +53,14 @@
</tr>
<?php endforeach ?>
</table>
+
+<?php if (Helper\is_admin()): ?>
+<div class="page-header">
+ <h2><?= t('Integration') ?></h2>
+</div>
+
+<h3><i class="fa fa-github fa-fw"></i><?= t('Github webhook') ?></h3>
+<input type="text" readonly="readonly" value="<?= Helper\get_current_base_url().Helper\u('webhook', 'github', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
+<p class="form-help"><a href="http://kanboard.net/documentation/github-webhooks" target="_blank"><?= t('Help on Github webhook') ?></a></p>
+
+<?php endif ?>
diff --git a/docs/automatic-actions.markdown b/docs/automatic-actions.markdown
index 631919ea..cf350a2f 100644
--- a/docs/automatic-actions.markdown
+++ b/docs/automatic-actions.markdown
@@ -32,6 +32,13 @@ List of available events
- Closing a task
- Task creation or modification
- Task assignee change
+- Github commit received
+- Github issue opened
+- Github issue closed
+- Github issue reopened
+- Github issue assignee change
+- Github issue label change
+- Github issue comment created
List of available actions
-------------------------
@@ -44,6 +51,10 @@ List of available actions
- Assign a color to a specific user
- Assign automatically a color based on a category
- Assign automatically a category based on a color
+- Create a task from an external provider
+- Change the assignee based on an external username
+- Change the category based on an external label
+- Create a comment from an external provider
Examples
--------
diff --git a/docs/github-webhooks.markdown b/docs/github-webhooks.markdown
new file mode 100644
index 00000000..cdbb69c9
--- /dev/null
+++ b/docs/github-webhooks.markdown
@@ -0,0 +1,94 @@
+Github webhook integration
+==========================
+
+Kanboard can be synchronized with Github.
+Currently, it's only a one-way synchronization: Github to Kanboard.
+
+Github webhooks are plugged to Kanboard automatic actions.
+When an event occurs on Github, an action can be performed on Kanboard.
+
+List of available events
+------------------------
+
+- Github commit received
+- Github issue opened
+- Github issue closed
+- Github issue reopened
+- Github issue assignee change
+- Github issue label change
+- Github issue comment created
+
+List of available actions
+-------------------------
+
+- Create a task from an external provider
+- Change the assignee based on an external username
+- Change the category based on an external label
+- Create a comment from an external provider
+
+Configuration on Github
+-----------------------
+
+Go to your project settings page, on the left choose "Webhooks & Services", then click on the button "Add webhook".
+
+![Github configuration](http://kanboard.net/screenshots/documentation/github-webhooks.png)
+
+- **Payload url**: This url is available on Kanboard, go to "projects", select your project, at the bottom of the page, you have a section Github webhooks, just copy and paste the link.
+- Select **"Send me everything"**
+
+Each time an event happens, Github will send an event to Kanboard now.
+The Kanboard webhook url is protected by a random token.
+
+Everything else is handled by automatic actions in your Kanboard project settings.
+
+Examples
+--------
+
+### Close a Kanboard task when a commit pushed to Github
+
+- Choose the event: **Github commit received**
+- Choose the action: **Close the task**
+
+When one or more commits are sent to Github, 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
+
+### Create a Kanboard task when a new issue is opened on Github
+
+- Choose the event: **Github issue opened**
+- Choose the action: **Create a task from an external provider**
+
+When a task is created from a Github issue, the link to the issue is added to the description and the task have a new field named "Reference" (this is the Github ticket number).
+
+### Close a Kanboard task when an issue is closed on Github
+
+- Choose the event: **Github issue closed**
+- Choose the action: **Close the task**
+
+### Reopen a Kanboard task when an issue is reopened on Github
+
+- Choose the event: **Github issue reopened**
+- Choose the action: **Open the task**
+
+### Assign a task to a Kanboard user when an issue is assigned on Github
+
+- Choose the event: **Github issue assignee change**
+- Choose the action: **Change the assignee based on an external username**
+
+Note: The username must be the same between Github and Kanboard.
+
+### Assign a category when an issue is tagged on Github
+
+- Choose the event: **Github issue label change**
+- Choose the action: **Change the category based on an external label**
+- Define the label and the category
+
+### Create a comment on Kanboard when an issue is commented on Github
+
+- Choose the event: **Github issue comment created**
+- Choose the action: **Create a comment from an external provider**
+
+Note: The username of the comment author must be the same between Github and Kanboard and the task must exists before.