- <?php
-
- namespace JiraPerforceSmartCommits;
-
- use Application\Config\ConfigManager;
- use Laminas\Http\Client as HttpClient;
- use Laminas\Json\Json;
- use Laminas\ServiceManager\ServiceLocatorInterface as ServiceLocator;
-
- class Module
- {
- public static function handleSmartCommitMessage($item, ServiceLocator $services)
- {
- $callouts = self::getJiraCallouts($item->getDescription());
- $config = $services->get('config');
- $logger = $services->get('logger');
-
- foreach ($callouts as $callout) {
- if (!array_key_exists('issues', $callout) || !array_key_exists('commands', $callout)) {
- continue;
- }
-
- foreach ($callout['issues'] as $issue) {
- $commentCommand = self::getCommand('comment', $callout['commands']);
- $timeCommand = self::getCommand('time', $callout['commands']);
- $transitionCommands = self::getTransitionCommands($callout['commands']);
-
- if ($commentCommand && array_key_exists('args', $commentCommand)) {
- $logger->info('JiraSmartCommits: building comment');
-
- if (ConfigManager::getValue($config, 'jirasmartcommits.cite_submitter_username', true)) {
- $prefix = "[~" . $item->getUser() . "] says in c";
- } else {
- $prefix = "C";
- }
-
- if (ConfigManager::getValue($config, 'jirasmartcommits.link_changelist_comment_reference', true)) {
- $qualifiedUrl = $services->get('ViewHelperManager')->get('qualifiedUrl');
- $changelist = "[" . $item->getId() . "|" . $qualifiedUrl('change', array('change' => $item->getId())) . "]";
- } else {
- $changelist = $item->getId();
- }
-
- $commentStr = "${prefix}hangelist $changelist: ${commentCommand['args']}";
- $commentObj = array(
- 'body' => $commentStr,
- );
- }
-
- if ($timeCommand && array_key_exists('args', $timeCommand)) {
- $logger->info('JiraSmartCommits: building time tracking');
-
- $msg = array(
- 'timeSpent' => $timeCommand['args'],
- );
-
- if (isset($commentStr)) {
- $logger->info('JiraSmartCommits: and bundling comment');
-
- $msg['comment'] = $commentStr;
- $commented = true;
- $timeCommented = true;
- }
-
- if (self::doRequest(
- 'post',
- "issue/$issue/worklog",
- $msg,
- $services) === false && isset($timeCommented)) {
- // if time-tracking fails but we wanted a comment, try to still get the comment posted
- unset($commented);
- }
-
- unset($timeCommented);
- }
-
- if (count($transitionCommands) > 0) {
- $logger->info('JiraSmartCommits: asked for transition(s)');
-
- $availableTransitions = self::doRequest(
- 'get',
- "issue/$issue/transitions",
- null,
- $services);
-
- $transitionId = null;
- if ($availableTransitions && is_array($availableTransitions) && array_key_exists('transitions', $availableTransitions)
- && is_array($availableTransitions['transitions'])) {
- foreach ($availableTransitions['transitions'] as $availableTransition) {
- if (is_array($availableTransition) && array_key_exists('name', $availableTransition)) {
- $availableTransitionName = str_replace(' ', '-', strtolower($availableTransition['name']));
- $logger->debug("JiraSmartCommits: available transition: $availableTransitionName");
-
- foreach ($transitionCommands as $transitionCommand) {
- $logger->debug("JiraSmartCommits: comparing to ${transitionCommand['command']}");
-
- if (strpos($availableTransitionName, $transitionCommand['command']) === 0) {
- $transitionId = $availableTransition['id'];
- break;
- }
- }
-
- if ($transitionId) {
- break;
- }
- }
- }
- }
-
- if ($transitionId) {
- $logger->info("JiraSmartCommits: found desired transition id $transitionId");
-
- $msg = array(
- 'transition' => array(
- 'id' => $transitionId,
- ),
- );
-
- if (isset($commentObj) && !isset($commented)) {
- $logger->info('JiraSmartCommits: bundling comment');
-
- $msg['update'] = array(
- 'comment' => array(
- array(
- 'add' => $commentObj,
- ),
- ),
- );
- $commented = true;
- $transitionCommented = true;
- }
-
- if (self::doRequest(
- 'post',
- "issue/$issue/transitions",
- $msg,
- $services) === false && isset($transitionCommented)) {
- // if resolving fails but we wanted a comment, try to still get the comment posted
- unset($commented);
- }
- } else {
- $logger->notice('JiraSmartCommits: unable to locate a valid transition id. ' . print_r($transitionCommands, true) . print_r($availableTransitions, true));
- }
-
- unset($transitionCommented);
- }
-
- if (isset($commentObj) && !isset($commented)) {
- $logger->info('JiraSmartCommits: comment wanted, not already handled. handling.');
-
- self::doRequest(
- 'post',
- "issue/$issue/comment",
- $commentObj,
- $services);
- }
-
- unset($commented, $commentObj, $commentStr);
- }
- }
- }
-
- private static function getCommand($key, $commandArray)
- {
- foreach ($commandArray as $command) {
- if (is_array($command) && array_key_exists('command', $command) && $command['command'] == $key) {
- return $command;
- }
- }
-
- return null;
- }
-
- private static function getTransitionCommands($commandArray)
- {
- $ret = array();
-
- foreach ($commandArray as $command) {
- if (is_array($command) && array_key_exists('command', $command) && $command['command'] != 'comment' && $command['command'] != 'time') {
- array_push($ret, $command);
- }
- }
-
- return $ret;
- }
-
- public static function getJiraCallouts($value)
- {
- $projects = array_map('preg_quote', self::getProjects());
- $callouts = array();
-
- foreach (explode("\n", $value) as $line) {
- $mode = 0;
- foreach (preg_split('/(\s+)/', $line) as $word) {
- $issues = self::getJiraIssue($word, $projects);
- $command = self::getSmartCommitCommand($word);
-
- if (!isset($last)) {
- $last = array();
- }
- if (count($callouts) > 0) {
- $last = &$callouts[count($callouts)-1];
- }
-
- if ($command && ($mode === 1 || $mode === 2)) {
- if (array_key_exists('commands', $last)) {
- $last['commands'][] = array('command' => $command);
- } else {
- $last['commands'] = array(array('command' => $command));
- }
-
- $mode = 2;
- } elseif ($issues) {
- if ($mode === 0 || !array_key_exists('issues', $last) || array_key_exists('commands', $last)) {
- $callouts[] = array('issues' => $issues);
- } else {
- $last['issues'] = array_merge($last['issues'], $issues);
- }
-
- $mode = 1;
- } elseif ($mode === 2) {
- $lastCommand = &$last['commands'][count($last['commands'])-1];
-
- if (array_key_exists('args', $lastCommand)) {
- $lastCommand['args'] .= ' ';
- } else {
- $lastCommand['args'] = '';
- }
-
- $lastCommand['args'] .= $word;
- }
- }
- }
-
- return $callouts;
- }
-
- public static function getJiraIssue($value, $projects)
- {
- if (preg_match_all("/((?:" . implode('|', $projects) . ")-[0-9]+)/", $value, $match)) {
- return $match[1];
- }
-
- return null;
- }
-
- public static function getSmartCommitCommand($value)
- {
- if (strpos($value, '#') === 0 && strlen($value) > 1) {
- return strtolower(substr($value, 1));
- }
-
- return null;
- }
-
- public static function doRequest($method, $resource, $data, ServiceLocator $services)
- {
- // we commonly do a number of requests and don't want one failure to bork them all,
- // if anything goes wrong just log it
- try {
- // setup the client and request details
- $config = $services->get('config');
- $url = ConfigManager::getValue($config, 'jirasmartcommits.host') . '/rest/api/latest/' . $resource;
- $client = new HttpClient;
- $client->setUri($url)
- ->setHeaders(array('Content-Type' => 'application/json'))
- ->setMethod($method);
-
- // set the http client options; including any special overrides for our host
- $options = $services->get('config') + array('http_client_options' => array());
- $options = (array) $options['http_client_options'];
- if (isset($options['hosts'][$client->getUri()->getHost()])) {
- $options = (array) $options['hosts'][$client->getUri()->getHost()] + $options;
- }
- unset($options['hosts']);
- $client->setOptions($options);
-
- if ($method == 'post') {
- $client->setRawBody(Json::encode($data));
- } else {
- $client->setParameterGet((array) $data);
- }
-
- if (ConfigManager::getValue($config, 'jirasmartcommits.user')) {
- $client->setAuth(ConfigManager::getValue($config, 'jirasmartcommits.user'), ConfigManager::getValue($config, 'jirasmartcommits.password'));
- }
-
- // attempt the request and log any errors
- $services->get('logger')->info('JiraSmartCommits making ' . $method . ' request to resource: ' . $url, (array) $data);
- $response = $client->dispatch($client->getRequest());
- if (!$response->isSuccess()) {
- $services->get('logger')->err(
- 'JiraSmartCommits failed to ' . $method . ' resource: ' . $url . ' (' .
- $response->getStatusCode() . " - " . $response->getReasonPhrase() . ').',
- array(
- 'request' => $client->getLastRawRequest(),
- 'response' => $client->getLastRawResponse()
- )
- );
- return false;
- }
-
- // looks like it worked, return the result
- return json_decode($response->getBody(), true);
- } catch (\Exception $e) {
- // the Jira module is doing this, but it seems to be generating its own exceptions for me.
- // fortunately the error is already logged from above, so we're good.
- //$services->get('logger')->err($e);
- }
-
- return false;
- }
-
- public static function getProjects()
- {
- $file = DATA_PATH . '/cache/jirasmartcommits/projects';
- if (!file_exists($file)) {
- return array();
- }
-
- return (array) json_decode(file_get_contents($file), true);
- }
-
- public static function getCacheDir()
- {
- $dir = DATA_PATH . '/cache/jirasmartcommits';
- if (!is_dir($dir)) {
- @mkdir($dir, 0700, true);
- }
- if (!is_writable($dir)) {
- @chmod($dir, 0700);
- }
- if (!is_dir($dir) || !is_writable($dir)) {
- throw new \RuntimeException(
- "Cannot write to cache directory ('" . $dir . "'). Check permissions."
- );
- }
-
- return $dir;
- }
-
- public function getConfig()
- {
- return include __DIR__ . '/config/module.config.php';
- }
- }