Add a command to assign release versions to Jira tickets [MAILPOET-1783]
This commit is contained in:
45
RoboFile.php
45
RoboFile.php
@@ -588,6 +588,16 @@ class RoboFile extends \Robo\Tasks {
|
||||
);
|
||||
}
|
||||
|
||||
protected function getReleaseVersionController($project) {
|
||||
require_once './tasks/release/ReleaseVersionController.php';
|
||||
$this->loadEnv();
|
||||
return \MailPoetTasks\Release\ReleaseVersionController::createWithJiraCredentials(
|
||||
getenv('WP_JIRA_TOKEN'),
|
||||
getenv('WP_JIRA_USER'),
|
||||
$project
|
||||
);
|
||||
}
|
||||
|
||||
public function testAcceptanceGroupTests() {
|
||||
return $this->taskSplitTestFilesByGroups(4)
|
||||
->projectRoot('.')
|
||||
@@ -598,10 +608,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
|
||||
public function writeReleaseVersion($version) {
|
||||
$version = trim($version);
|
||||
if (!preg_match('/\d+\.\d+\.\d+/', $version)) {
|
||||
$this->yell('Incorrect version format', 40, 'red');
|
||||
exit(1);
|
||||
}
|
||||
$this->validateVersion($version);
|
||||
|
||||
$this->taskReplaceInFile(__DIR__ . '/readme.txt')
|
||||
->regex('/Stable tag:\s*\d+\.\d+\.\d+/i')
|
||||
@@ -618,4 +625,34 @@ class RoboFile extends \Robo\Tasks {
|
||||
->to(sprintf("'version' => '%s',", $version))
|
||||
->run();
|
||||
}
|
||||
|
||||
public function jiraReleaseVersion($opts = ['free' => null, 'premium' => null]) {
|
||||
require_once './tasks/release/Jira.php';
|
||||
if (empty($opts['free']) && empty($opts['premium'])) {
|
||||
$this->yell('No Free or Premium version specified', 40, 'red');
|
||||
exit(1);
|
||||
}
|
||||
$output = [];
|
||||
if (!empty($opts['free'])) {
|
||||
$this->validateVersion($opts['free']);
|
||||
$output[] = $this->getReleaseVersionController(\MailPoetTasks\Release\Jira::PROJECT_MAILPOET)
|
||||
->assignVersionToCompletedTickets($opts['free']);
|
||||
}
|
||||
if (!empty($opts['premium'])) {
|
||||
$this->validateVersion($opts['premium']);
|
||||
$output[] = $this->getReleaseVersionController(\MailPoetTasks\Release\Jira::PROJECT_PREMIUM)
|
||||
->assignVersionToCompletedTickets($opts['premium']);
|
||||
}
|
||||
if($opts['quiet']) {
|
||||
return;
|
||||
}
|
||||
$this->say(join("\n", $output));
|
||||
}
|
||||
|
||||
private function validateVersion($version) {
|
||||
if (!preg_match('/\d+\.\d+\.\d+/', $version)) {
|
||||
$this->yell('Incorrect version format', 40, 'red');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ class Jira {
|
||||
const PROJECT_PREMIUM = 'PREMIUM';
|
||||
|
||||
const JIRA_DOMAIN = 'mailpoet.atlassian.net';
|
||||
const JIRA_API_VERSION = '3';
|
||||
|
||||
/** @var string */
|
||||
private $token;
|
||||
@@ -31,7 +32,7 @@ class Jira {
|
||||
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-project-projectIdOrKey-versions-get
|
||||
*/
|
||||
function getVersion($version_name = null) {
|
||||
$versions = $this->fetchFromJira("project/$this->project/versions");
|
||||
$versions = $this->makeJiraRequest("project/$this->project/versions");
|
||||
if ($version_name === null) {
|
||||
return end($versions);
|
||||
}
|
||||
@@ -44,12 +45,22 @@ class Jira {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-search-get
|
||||
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-version-post
|
||||
*/
|
||||
function createVersion($version_name) {
|
||||
$data = [
|
||||
'name' => $version_name,
|
||||
'archived' => false,
|
||||
'released' => false,
|
||||
'project' => $this->project,
|
||||
];
|
||||
return $this->makeJiraRequest('/version', 'POST', $data);
|
||||
}
|
||||
|
||||
function getIssuesDataForVersion($version) {
|
||||
$changelog_id = self::CHANGELOG_FIELD_ID;
|
||||
$release_note_id = self::RELEASENOTE_FIELD_ID;
|
||||
$issues_data = $this->fetchFromJira("/search?fields=key,$changelog_id,$release_note_id,status&jql=fixVersion={$version['id']}");
|
||||
$issues_data = $this->search("fixVersion={$version['id']}", ['key', $changelog_id, $release_note_id, 'status']);
|
||||
// Sort issues by importance of change (Added -> Updated -> Improved -> Changed -> Fixed -> Others)
|
||||
usort($issues_data['issues'], function($a, $b) use ($changelog_id) {
|
||||
$order = array_flip(['added', 'updat', 'impro', 'chang', 'fixed']);
|
||||
@@ -62,16 +73,46 @@ class Jira {
|
||||
return $issues_data['issues'];
|
||||
}
|
||||
|
||||
private function fetchFromJira($path) {
|
||||
/**
|
||||
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-search-get
|
||||
*/
|
||||
function search($jql, array $fields = null) {
|
||||
$params = ['jql' => $jql];
|
||||
if ($fields) {
|
||||
$params['fields'] = join(',', $fields);
|
||||
}
|
||||
return $this->makeJiraRequest("/search?" . http_build_query($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-put
|
||||
*/
|
||||
function updateIssue($key, $data) {
|
||||
return $this->makeJiraRequest("/issue/$key", 'PUT', $data);
|
||||
}
|
||||
|
||||
private function makeJiraRequest($path, $method = 'GET', array $data = null) {
|
||||
$url_user = urlencode($this->user);
|
||||
$url_token = urlencode($this->token);
|
||||
$jira_domain = self::JIRA_DOMAIN;
|
||||
$jira_url = "https://$url_user:$url_token@$jira_domain/rest/api/3/$path";
|
||||
$data = file_get_contents($jira_url);
|
||||
if ($data === false) {
|
||||
$jira_api_version = self::JIRA_API_VERSION;
|
||||
$jira_url = "https://$url_user:$url_token@$jira_domain/rest/api/$jira_api_version/$path";
|
||||
$options = [];
|
||||
if ($method === 'POST' || $method === 'PUT') {
|
||||
$options = [
|
||||
'http' => [
|
||||
'method' => $method,
|
||||
'header' => "Content-type: application/json\r\n",
|
||||
'content' => json_encode($data),
|
||||
]
|
||||
];
|
||||
}
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($jira_url, false, $context);
|
||||
if ($result === false) {
|
||||
$error = error_get_last();
|
||||
throw new \Exception('JIRA request error: ' . $error['message']);
|
||||
}
|
||||
return json_decode($data, true);
|
||||
return json_decode($result, true);
|
||||
}
|
||||
}
|
||||
|
81
tasks/release/ReleaseVersionController.php
Normal file
81
tasks/release/ReleaseVersionController.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoetTasks\Release;
|
||||
|
||||
require_once __DIR__ . '/Jira.php';
|
||||
|
||||
class ReleaseVersionController {
|
||||
|
||||
/** @var Jira */
|
||||
private $jira;
|
||||
|
||||
/** @var string */
|
||||
private $project;
|
||||
|
||||
function __construct(Jira $jira, $project) {
|
||||
$this->jira = $jira;
|
||||
$this->project = $project;
|
||||
}
|
||||
|
||||
static function createWithJiraCredentials($token, $user, $project) {
|
||||
return new self(new Jira($token, $user, $project), $project);
|
||||
}
|
||||
|
||||
function assignVersionToCompletedTickets($version) {
|
||||
$output = [];
|
||||
$output[] = "Checking version $version in $this->project";
|
||||
|
||||
if (!$this->checkVersion($version)) {
|
||||
$output[] = "The version is invalid or already released";
|
||||
return join("\n", $output);
|
||||
}
|
||||
|
||||
$output[] = "Setting version $version to completed tickets in $this->project...";
|
||||
$issues = $this->getDoneIssuesWithoutVersion();
|
||||
$result = array_map(function ($issue) use ($version) {
|
||||
return $this->setIssueFixVersion($issue['key'], $version);
|
||||
}, $issues);
|
||||
$output[] = "Done, issues processed: " . count($result);
|
||||
|
||||
return join("\n", $output);
|
||||
}
|
||||
|
||||
function getDoneIssuesWithoutVersion() {
|
||||
$jql = "project = $this->project AND status = Done AND (fixVersion = EMPTY OR fixVersion IN unreleasedVersions()) AND updated >= -52w";
|
||||
$result = $this->jira->search($jql, ['key']);
|
||||
return array_map(function ($issue) {
|
||||
return [
|
||||
'id' => $issue['id'],
|
||||
'key' => $issue['key'],
|
||||
];
|
||||
}, $result['issues']);
|
||||
}
|
||||
|
||||
function checkVersion($version) {
|
||||
try {
|
||||
$version_data = $this->jira->getVersion($version);
|
||||
} catch (\Exception $e) {
|
||||
$version_data = false;
|
||||
}
|
||||
if (!empty($version_data['released'])) {
|
||||
// version is already released
|
||||
return false;
|
||||
} else if (empty($version_data)) {
|
||||
// version does not exist
|
||||
$this->jira->createVersion($version);
|
||||
}
|
||||
// version exists
|
||||
return true;
|
||||
}
|
||||
|
||||
function setIssueFixVersion($issue_key, $version) {
|
||||
$data = [
|
||||
'update' => [
|
||||
'fixVersions' => [
|
||||
['set' => [['name' => $version]]]
|
||||
]
|
||||
]
|
||||
];
|
||||
return $this->jira->updateIssue($issue_key, $data);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user