From 51b8b77b598ea8ab1633458361cd3c722a54df96 Mon Sep 17 00:00:00 2001 From: Liam Morland Date: Wed, 30 Jun 2021 14:42:40 -0400 Subject: [PATCH 1/6] ISTWCMS-4928: Use Jira Personal Access Token (PAT) instead of username and password Create uw_wcms_tools_get_jira_credentials(). Take credentials from environment variables when function variable_get() does not exist. Create constant UW_WCMS_TOOLS_JIRA_API_LOCATION. --- uw_wcms_tools.jira.inc | 55 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/uw_wcms_tools.jira.inc b/uw_wcms_tools.jira.inc index 42920ca..0d34da4 100644 --- a/uw_wcms_tools.jira.inc +++ b/uw_wcms_tools.jira.inc @@ -9,6 +9,28 @@ */ define('UW_WCMS_TOOLS_JIRA_URL_PREFIX', 'https://jira.uwaterloo.ca/browse/'); +define('UW_WCMS_TOOLS_JIRA_API_LOCATION', 'https://jira.uwaterloo.ca/rest/api/2/'); + +/** + * Return the Jira Personal Access Token (PAT). + * + * Call variable_get() if it exists, otherwise use environment variables. + * + * @return string + * The token. + */ +function uw_wcms_tools_get_jira_credentials(): string { + if (function_exists('variable_get')) { + $jira_access_token = variable_get('jira_access_token'); + } + else { + $jira_access_token = getenv('JIRA_ACCESS_TOKEN'); + } + if (!$jira_access_token) { + throw new Exception('Jira API credentials not configured.'); + } + return $jira_access_token; +} /** * Run a query on the Jira API. @@ -28,27 +50,26 @@ function uw_wcms_tools_jira_api_query($path, stdClass $data = NULL, $method = 'P throw new Exception('Unsupported HTTP method.'); } - $jira_username = variable_get('jira_username'); - $jira_password = variable_get('jira_password'); - if (!$jira_username || !$jira_password) { - throw new Exception('Jira API credentials not configured.'); - } + // Method is GET unless there is data. + $options = [ + 'http' => [ + 'method' => 'GET', + 'header' => [ + 'Authorization: Bearer ' . uw_wcms_tools_get_jira_credentials(), + ], + ], + ]; if ($data) { - $opts = [ - 'http' => [ - 'method' => $method, - 'header' => 'Content-Type: application/json', - 'content' => json_encode($data), - ], - ]; - $context = stream_context_create($opts); - } - else { - $context = NULL; + $options['http']['method'] = $method; + $options['http']['header'][] = 'Content-Type: application/json'; + $options['http']['content'] = json_encode($data); } - $url = 'https://' . $jira_username . ':' . $jira_password . '@jira.uwaterloo.ca/rest/api/2/' . $path; + $options['http']['header'] = implode("\r\n", $options['http']['header']); + $context = stream_context_create($options); + + $url = UW_WCMS_TOOLS_JIRA_API_LOCATION . $path; $results = file_get_contents($url, FALSE, $context); if ($results) { return json_decode($results); -- GitLab From 5095110a4179751454a4e59ad18866464152757b Mon Sep 17 00:00:00 2001 From: Liam Morland Date: Wed, 30 Jun 2021 16:09:18 -0400 Subject: [PATCH 2/6] ISTWCMS-4928: Create uw_wcms_tools_gitlab_get_file() --- uw_wcms_tools.gitlab.inc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/uw_wcms_tools.gitlab.inc b/uw_wcms_tools.gitlab.inc index 39b010f..f76b142 100644 --- a/uw_wcms_tools.gitlab.inc +++ b/uw_wcms_tools.gitlab.inc @@ -916,3 +916,31 @@ function uw_wcms_tools_gitlab_get_profile_projects(): array { } return $profile_projects; } + +/** + * Get a file from a GitLab repository. + * + * @param string $project + * The repository to load from. + * @param string $file_path + * The file to load. + * @param string $ref + * The ref of the file to load. + * + * @return array + * The GitLab API response. + */ +function uw_wcms_tools_gitlab_get_file(string $project, string $file_path, string $ref): array { + $id = urlencode($project); + $path = urlencode($file_path); + $query = [ + 'ref' => $ref, + ]; + $result = uw_wcms_tools_query_gitlab_api('projects/' . $id . '/repository/files/' . $path, $query); + + if (isset($result['body']->content)) { + $result['body']->content = base64_decode($result['body']->content); + } + + return $result; +} -- GitLab From c90a1f18bb3210954752214ba82e3865be5c1cbc Mon Sep 17 00:00:00 2001 From: Liam Morland Date: Wed, 30 Jun 2021 16:24:28 -0400 Subject: [PATCH 3/6] ISTWCMS-4928: Create uw_wcms_tools_gitlab_get_commits() --- uw_wcms_tools.gitlab.inc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/uw_wcms_tools.gitlab.inc b/uw_wcms_tools.gitlab.inc index f76b142..a095591 100644 --- a/uw_wcms_tools.gitlab.inc +++ b/uw_wcms_tools.gitlab.inc @@ -944,3 +944,30 @@ function uw_wcms_tools_gitlab_get_file(string $project, string $file_path, strin return $result; } + +/** + * List commits in a GitLab repository. + * + * @param string $project + * The repository to load from. + * @param string $start + * The ref to load commits from or the start of the series of commits to load. + * @param string $end + * The end of the series of commits to load. + * + * @return array + * The GitLab API response. + */ +function uw_wcms_tools_gitlab_get_commits(string $project, string $start, string $end = NULL): array { + $ref_name = $start; + if ($end) { + $ref_name .= '..' . $end; + } + + $id = urlencode($project); + $query = [ + 'ref_name' => $ref_name, + 'per_page' => 100, + ]; + return uw_wcms_tools_query_gitlab_api('projects/' . $id . '/repository/commits', $query); +} -- GitLab From 9dfd0b796abd4fa00db3ff2d1ede71b0c445b6fd Mon Sep 17 00:00:00 2001 From: Liam Morland Date: Tue, 6 Jul 2021 10:41:21 -0400 Subject: [PATCH 4/6] ISTWCMS-4928: Create uw_wcms_tools_gitlab_get_project_commits() --- uw_wcms_tools.gitlab.inc | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/uw_wcms_tools.gitlab.inc b/uw_wcms_tools.gitlab.inc index a095591..54218fa 100644 --- a/uw_wcms_tools.gitlab.inc +++ b/uw_wcms_tools.gitlab.inc @@ -971,3 +971,41 @@ function uw_wcms_tools_gitlab_get_commits(string $project, string $start, string ]; return uw_wcms_tools_query_gitlab_api('projects/' . $id . '/repository/commits', $query); } + +/** + * Add commits between two revisions to an array of projects. + * + * @param array $projects + * An array of arrays of revisions keyed by project name. + */ +function uw_wcms_tools_gitlab_get_project_commits(array &$projects): void { + foreach ($projects as $name => $versions) { + // Exclude non-WCMS projets. + list($group) = explode('/', $name, 2); + if ($group !== 'wcms') { + // Indicate that lookup was not attempted. + $projects[$name]['commits'] = NULL; + continue; + } + + $projects[$name]['commits'] = []; + + // Exclude projects that are not upgrades. + if (!$versions['start'] || !$versions['end'] || $versions['start'] === $versions['end']) { + continue; + } + + $commits = uw_wcms_tools_gitlab_get_commits($name, $versions['start'], $versions['end']); + if ($commits['http_status'] !== 200) { + throw new Exception('Unable to load commits.'); + } + foreach ($commits['body'] as $commit) { + // @todo Replace with str_starts_with() after upgrade to PHP 8. + if (strncmp($commit->title, 'Merge branch', 12) === 0) { + continue; + } + + $projects[$name]['commits'][] = $commit; + } + } +} -- GitLab From 88e99a613a100d062937dfd2cae283352ac2c400 Mon Sep 17 00:00:00 2001 From: Liam Morland Date: Tue, 6 Jul 2021 10:41:52 -0400 Subject: [PATCH 5/6] ISTWCMS-4928: Create release-notes.php --- release-notes.php | 18 +++++ uw_wcms_tools.releasenotes.inc | 124 +++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100755 release-notes.php create mode 100644 uw_wcms_tools.releasenotes.inc diff --git a/release-notes.php b/release-notes.php new file mode 100755 index 0000000..0dae5d3 --- /dev/null +++ b/release-notes.php @@ -0,0 +1,18 @@ +#!/usr/bin/env php + $start, + 'end' => $end, + ]; + + // Load uw_base_profile composer.json from the start and end revision. Make an + // array of required project names and the version of each at the start and + // end revisions. + $projects = []; + foreach (array_keys($tags) as $stage) { + $composer = uw_wcms_tools_gitlab_get_file('wcms/uw_base_profile', 'composer.json', $tags[$stage]); + if ($composer['http_status'] !== 200) { + throw new Exception('Unable to load composer.json.'); + } + $composer = json_decode($composer['body']->content); + + foreach ($composer->require as $name => $version) { + $projects[$name][$stage] = $version; + } + } + + // Generate a list of projects that have been updated. + $release_notes = "Projects updated\n"; + foreach ($projects as $name => &$versions) { + foreach (array_keys($tags) as $stage) { + $versions[$stage] = $versions[$stage] ?? NULL; + } + + if ($versions['start'] === $versions['end']) { + continue; + } + + $release_notes .= sprintf('%-40s: ', $name); + + if (!$versions['start']) { + $release_notes .= 'Added'; + } + elseif (!$versions['end']) { + $release_notes .= 'Removed'; + } + else { + $release_notes .= sprintf('Upgraded from %s to %s', $versions['start'], $versions['end']); + } + + $release_notes .= "\n"; + } + + $release_notes .= "\n"; + + uw_wcms_tools_gitlab_get_project_commits($projects); + + // Generate a list of updated WCMS projects and the commits each has had. + $release_notes .= "Updates to WCMS projects\n"; + foreach ($projects as $path => $commits) { + // Skip projects with no commits. + if (!$commits['commits']) { + continue; + } + list(, $project) = explode('/', $path, 2); + $release_notes .= $project . "\n"; + foreach ($commits['commits'] as $commit) { + $release_notes .= "\t" . $commit->title . "\n"; + } + } + + $release_notes .= "\n"; + + // Generate a list of Jira tickets acted on. + $release_notes .= "Tickets acted on\n"; + $tickets = []; + // Make array with keys being the ticket numbers from two-dimensional array of + // commits. + foreach ($projects as $path => $commits) { + if (!$commits['commits']) { + continue; + } + foreach ($commits['commits'] as $commit) { + if (preg_match('/ISTWCMS-\d+/', $commit->title, $matches)) { + foreach ($matches as $match) { + $tickets[$match] = TRUE; + } + } + } + } + $tickets = array_keys($tickets); + // Sort tickets and output with titles from Jira. + sort($tickets); + $use_jira = TRUE; + foreach ($tickets as $ticket) { + $release_notes .= $ticket; + // If Jira API is available, show ticket titles. On failure, do not re-try. + if ($use_jira) { + $jira_ticket = @uw_wcms_tools_jira_api_query('issue/' . $ticket); + if (isset($jira_ticket->fields->summary)) { + $release_notes .= ': ' . $jira_ticket->fields->summary; + } + else { + $use_jira = FALSE; + } + } + $release_notes .= "\n"; + } + + return $release_notes; +} -- GitLab From 2e5aec18607d20a49a2028ce09f2609e9d059090 Mon Sep 17 00:00:00 2001 From: Kevin Paxman Date: Mon, 19 Jul 2021 09:58:33 -0400 Subject: [PATCH 6/6] ISTWCMS-4928: fix minor typo in comment --- uw_wcms_tools.gitlab.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uw_wcms_tools.gitlab.inc b/uw_wcms_tools.gitlab.inc index 54218fa..b3ab573 100644 --- a/uw_wcms_tools.gitlab.inc +++ b/uw_wcms_tools.gitlab.inc @@ -980,7 +980,7 @@ function uw_wcms_tools_gitlab_get_commits(string $project, string $start, string */ function uw_wcms_tools_gitlab_get_project_commits(array &$projects): void { foreach ($projects as $name => $versions) { - // Exclude non-WCMS projets. + // Exclude non-WCMS projects. list($group) = explode('/', $name, 2); if ($group !== 'wcms') { // Indicate that lookup was not attempted. -- GitLab