#!/usr/bin/env php $value) { if ($callback($value, $key)) { return true; } } return false; } } function get_jira(array $statuses): array { $padlen = 50; print(str_pad("\rGetting tickets from jira...", $padlen)); $statuses = implode(' ', array_map(fn($x)=>"-s'$x'", $statuses)); $ticket_lines = explode("\n", rtrim(shell_exec("jira-cli issues list -q'project IS NOT EMPTY and project != AUTO and project != NET and project != MSP and project != SYST and project != LOYAL and Sprint in openSprints()' --order-by='Sprint' $statuses --columns=KEY,LABELS --plain | grep -v '^KEY\b'"))); $tickets = array_map(fn($x) => explode("\t", $x)[0], $ticket_lines); $ticketLabels = array_combine( $tickets, array_map(fn($x) => explode(',', explode("\t", $x)[1] ?? ''), $ticket_lines) ); return [$tickets, $ticketLabels]; } [$tickets, $ticketLabels] = get_jira(['Code Review']); function get_gh(string $repo, bool $cache = false): array { $padlen = 50; $params = 'title,url,statusCheckRollup,reviews,author,reviewDecision,reviewRequests,headRefName,mergeable,additions,deletions'; print(str_pad("\rGetting $repo PR's from github...", $padlen)); if ($cache == false) { try { $data = json_decode(shell_exec("gh pr list -L 75 -R BetterCarPeople/$repo --json '$params'"), true); if (array_filter($data)) { file_put_contents("/tmp/prs_$repo.json", json_encode($data)); return $data; } } catch (\Throwable $e) { try { sleep(1); // Try again one more time (because github's API works better hot) $data = json_decode(shell_exec("gh pr list -L 75 -R BetterCarPeople/$repo --json '$params'"), true); if (array_filter($data)) { file_put_contents("/tmp/prs_$repo.json", json_encode($data)); return $data; } } catch (\Throwable $e) { } } } $file = fopen("/tmp/prs_$repo.json", 'r'); if (! $file) { print("\nThere is no backup file to use for {$repo}.\n"); exit(1); } $date = (new DateTime('@'.fstat($file)['mtime'])) ->setTimezone(new DateTimeZone(date_default_timezone_get())) ->format('Y-m-d H:i:s T'); print("\n{$repo}: Using backup file modified on " . $date . "\n"); return json_decode(shell_exec("cat /tmp/prs_$repo.json"), true) ?? []; } $use_cache = false; if ($argv[1] == "--cached") { $use_cache = true; } $prs_jsons = []; $prs_jsons[] = get_gh("OnBDC2", $use_cache); //$prs_jsons[] = get_gh("maple-ai"); $prs_jsons[] = get_gh("maple", $use_cache); print(str_pad("\r", $padlen)); print("\r"); if (empty(array_filter($prs_jsons))) { print("Didn't get anything from Github\n"); exit(1); } $prs_json = array_merge(...$prs_jsons); $output = ""; $has_pr = []; file_put_contents("/tmp/jira-CR.html", ""); function process_pr($pr, $tickets, $ticketLabels, $onlyShowNeedsReview = false) { global $has_pr; $ansired = "\033[31m"; $ansigreen = "\033[32m"; $ansiyellow = "\033[33m"; $ansiblue = "\033[34m"; $ansicyan = "\033[36m"; $ansireset = "\033[0m"; $pr_ticket = null; $pr_ticket = in_array($pr['headRefName'], $tickets) ? $pr['headRefName'] : null; $pr_ticket ??= (preg_match("/^[^: ]*/", $pr['title'], $matches) && in_array($matches[0], $tickets)) ? $matches[0] : null; if ($pr_ticket) { $has_pr[$pr_ticket] = true; // These are github PR's that are in code review in Jira $title = str_pad($pr['title'], 40); if (strlen($title) > 40) { $title = substr($title,0,40-3)."..."; } $author = $pr['author']['login']; $labels = $ticketLabels[$pr_ticket] ?? []; $labelsText = trim( (in_array("Successful_Demonstration", $labels) ? ($ansiblue . "๐บ Demo'd " . $ansireset) : "") . (in_array("Hot", $labels) ? "๐ฅ Hot " : '') ); $plusminus = $ansigreen . "+" . $pr['additions'] . $ansired . "-" . $pr['deletions'] . $ansireset; $mergeable = $pr['mergeable'] == 'CONFLICTING' ? '๐ง: Merge conflicts. ' : ''; // Show pipeline status if failed $failedPipes = []; foreach ($pr['statusCheckRollup'] as $scr) { if ($scr['conclusion'] === 'FAILURE') { $failedPipes[] = $scr['name']; } } $failedPipes = array_unique($failedPipes); $failed = $failedPipes ? (str_repeat("๐งช๐", count($failedPipes)) . ": " . implode(", ", $failedPipes) . '. ') : ''; $failed = $failed ? ($ansicyan . $failed . $ansireset) : ""; $pipeline_status = $failed; // Show approvers $review_status = ""; $requestNames = []; foreach ($pr['reviewRequests'] as $rr) { if ($rr['__typename'] === "Team") { $requestNames[] = $rr['name']; } else { $requestNames[] = $rr['login']; } } $requestNames = array_unique($requestNames); $changeNames = []; foreach ($pr['reviews'] as $r) { if ($r['state'] === "CHANGES_REQUESTED" && ! in_array($r['author']['login'], $requestNames)) { $changeNames[] = $r['author']['login']; } } $changeNames = array_unique($changeNames); $approvers = []; foreach ($pr['reviews'] as $r) { if ($r['state'] === "APPROVED") { $approvers[] = $r['author']['login']; } } $approvers = array_unique($approvers); if ($onlyShowNeedsReview && count($approvers) >= 2) { return ['', '']; } // If the same name has both CHANGES_REQUESTED and APPROVED, then go with the newer of the two. foreach (array_intersect($changeNames, $approvers) as $name_in_both) { $both_reviews = array_filter($pr['reviews'], fn($r) => $r['author']['login'] == $name_in_both && in_array($r['state'], ["CHANGES_REQUESTED", "APPROVED"])); //usort($both_reviews, fn($a, $b) => $a['submittedAt'] <=> $b['submittedAt']); $last = end($both_reviews); //echo(json_encode($both_reviews)); if ($last['state'] == 'CHANGES_REQUESTED') { // Remove from approvers $approvers = array_filter($approvers, fn($n) => $n != $name_in_both); } elseif ($last['state'] == 'APPROVED') { // Remove from changeNames $changeNames = array_filter($changeNames, fn($n) => $n != $name_in_both); } } $commentNames = []; foreach ($pr['reviews'] as $r) { $newName = $r['author']['login']; if ($r['state'] === "COMMENTED" && $newName != $author && !array_any([...$approvers, ...$requestNames, ...$changeNames], fn($n)=>$n===$newName)) { $commentNames[] = $newName; } } $commentNames = array_unique($commentNames); $changes = $changeNames ? (str_repeat("โ", count($changeNames)) . ": " . implode(", ", $changeNames) . '. ') : ''; $changes = $changes ? ($ansired . $changes . $ansireset) : ''; $reviewRequests = $requestNames ? (str_repeat("๐ก", count($requestNames)) . ": " . implode(", ", $requestNames) . '. ') : ''; $reviewRequests = $reviewRequests ? ($ansiyellow . $reviewRequests . $ansireset) : ''; $approvals = $approvers ? (str_repeat("โ ", count($approvers)) . ": " . implode(", ", $approvers) . '. ') : ''; $approvals = $approvals ? ($ansigreen . $approvals . $ansireset) : ''; $comments = $commentNames ? (str_repeat("๐ฌ", count($commentNames)) . ": " . implode(", ", $commentNames) . '. ') : ''; $review_status = $changes . $reviewRequests . $approvals . $comments; $output = sprintf("\e]8;;$pr[url]?T=$pr_ticket\e\\$title\e]8;;\e\\"); $output .= "($author)$labelsText$plusminus$mergeable$pipeline_status$review_status\n"; $html_output = "