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() {
|
public function testAcceptanceGroupTests() {
|
||||||
return $this->taskSplitTestFilesByGroups(4)
|
return $this->taskSplitTestFilesByGroups(4)
|
||||||
->projectRoot('.')
|
->projectRoot('.')
|
||||||
@@ -598,10 +608,7 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
|
|
||||||
public function writeReleaseVersion($version) {
|
public function writeReleaseVersion($version) {
|
||||||
$version = trim($version);
|
$version = trim($version);
|
||||||
if (!preg_match('/\d+\.\d+\.\d+/', $version)) {
|
$this->validateVersion($version);
|
||||||
$this->yell('Incorrect version format', 40, 'red');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->taskReplaceInFile(__DIR__ . '/readme.txt')
|
$this->taskReplaceInFile(__DIR__ . '/readme.txt')
|
||||||
->regex('/Stable tag:\s*\d+\.\d+\.\d+/i')
|
->regex('/Stable tag:\s*\d+\.\d+\.\d+/i')
|
||||||
@@ -618,4 +625,34 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
->to(sprintf("'version' => '%s',", $version))
|
->to(sprintf("'version' => '%s',", $version))
|
||||||
->run();
|
->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 PROJECT_PREMIUM = 'PREMIUM';
|
||||||
|
|
||||||
const JIRA_DOMAIN = 'mailpoet.atlassian.net';
|
const JIRA_DOMAIN = 'mailpoet.atlassian.net';
|
||||||
|
const JIRA_API_VERSION = '3';
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $token;
|
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
|
* @see https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-project-projectIdOrKey-versions-get
|
||||||
*/
|
*/
|
||||||
function getVersion($version_name = null) {
|
function getVersion($version_name = null) {
|
||||||
$versions = $this->fetchFromJira("project/$this->project/versions");
|
$versions = $this->makeJiraRequest("project/$this->project/versions");
|
||||||
if ($version_name === null) {
|
if ($version_name === null) {
|
||||||
return end($versions);
|
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) {
|
function getIssuesDataForVersion($version) {
|
||||||
$changelog_id = self::CHANGELOG_FIELD_ID;
|
$changelog_id = self::CHANGELOG_FIELD_ID;
|
||||||
$release_note_id = self::RELEASENOTE_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)
|
// Sort issues by importance of change (Added -> Updated -> Improved -> Changed -> Fixed -> Others)
|
||||||
usort($issues_data['issues'], function($a, $b) use ($changelog_id) {
|
usort($issues_data['issues'], function($a, $b) use ($changelog_id) {
|
||||||
$order = array_flip(['added', 'updat', 'impro', 'chang', 'fixed']);
|
$order = array_flip(['added', 'updat', 'impro', 'chang', 'fixed']);
|
||||||
@@ -62,16 +73,46 @@ class Jira {
|
|||||||
return $issues_data['issues'];
|
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_user = urlencode($this->user);
|
||||||
$url_token = urlencode($this->token);
|
$url_token = urlencode($this->token);
|
||||||
$jira_domain = self::JIRA_DOMAIN;
|
$jira_domain = self::JIRA_DOMAIN;
|
||||||
$jira_url = "https://$url_user:$url_token@$jira_domain/rest/api/3/$path";
|
$jira_api_version = self::JIRA_API_VERSION;
|
||||||
$data = file_get_contents($jira_url);
|
$jira_url = "https://$url_user:$url_token@$jira_domain/rest/api/$jira_api_version/$path";
|
||||||
if ($data === false) {
|
$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();
|
$error = error_get_last();
|
||||||
throw new \Exception('JIRA request error: ' . $error['message']);
|
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