|
| 1 | +<?php |
| 2 | +declare(strict_types=1); |
| 3 | + |
| 4 | +/** |
| 5 | + * GitHub PR Code Review Queue — Listing CLI |
| 6 | + * |
| 7 | + * Read-only peek into the `codereview:queue` Redis list. Does NOT consume |
| 8 | + * (uses LRANGE, not RPOP). Safe to run while the worker is processing. |
| 9 | + * |
| 10 | + * Usage: |
| 11 | + * php scripts/github-code-review-list.php |
| 12 | + * php scripts/github-code-review-list.php --limit 20 |
| 13 | + * php scripts/github-code-review-list.php --json |
| 14 | + * php scripts/github-code-review-list.php --verbose |
| 15 | + * php scripts/github-code-review-list.php --repo owner/name |
| 16 | + * php scripts/github-code-review-list.php --metrics |
| 17 | + */ |
| 18 | + |
| 19 | +require_once __DIR__ . '/../vendor/autoload.php'; |
| 20 | +require_once __DIR__ . '/../src/CodeReviewQueue.php'; |
| 21 | + |
| 22 | +$envFile = __DIR__ . '/../.env'; |
| 23 | +if (file_exists($envFile)) { |
| 24 | + $dotenv = \Dotenv\Dotenv::createImmutable(dirname(__DIR__)); |
| 25 | + $dotenv->load(); |
| 26 | +} |
| 27 | + |
| 28 | +$opts = parseArgs($argv); |
| 29 | +if (!empty($opts['help'])) { |
| 30 | + printUsage(); |
| 31 | + exit(0); |
| 32 | +} |
| 33 | + |
| 34 | +$host = getenv('REDIS_HOST') ?: '67.217.60.234'; |
| 35 | +$port = (int)(getenv('REDIS_PORT') ?: 6379); |
| 36 | + |
| 37 | +try { |
| 38 | + $redis = new \Predis\Client([ |
| 39 | + 'scheme' => 'tcp', |
| 40 | + 'host' => $host, |
| 41 | + 'port' => $port, |
| 42 | + 'timeout' => 2.0, |
| 43 | + ]); |
| 44 | + $redis->connect(); |
| 45 | +} catch (\Throwable $e) { |
| 46 | + fwrite(STDERR, "Redis connect failed ({$host}:{$port}): {$e->getMessage()}\n"); |
| 47 | + exit(2); |
| 48 | +} |
| 49 | + |
| 50 | +$queueLen = (int)$redis->llen(CodeReviewQueue::QUEUE_KEY); |
| 51 | +$enqueued = (int)($redis->get(CodeReviewQueue::METRICS_KEY) ?? 0); |
| 52 | + |
| 53 | +if (!empty($opts['metrics'])) { |
| 54 | + echo json_encode([ |
| 55 | + 'queue_key' => CodeReviewQueue::QUEUE_KEY, |
| 56 | + 'queue_depth' => $queueLen, |
| 57 | + 'total_enqueued' => $enqueued, |
| 58 | + 'redis_host' => $host, |
| 59 | + 'redis_port' => $port, |
| 60 | + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; |
| 61 | + exit(0); |
| 62 | +} |
| 63 | + |
| 64 | +// LRANGE 0 N-1 — newest-first because workers RPOP and producers LPUSH |
| 65 | +$limit = isset($opts['limit']) ? max(1, (int)$opts['limit']) : 50; |
| 66 | +$stop = $limit - 1; |
| 67 | +$raw = $redis->lrange(CodeReviewQueue::QUEUE_KEY, 0, $stop); |
| 68 | + |
| 69 | +$entries = []; |
| 70 | +foreach ($raw as $idx => $json) { |
| 71 | + $env = json_decode($json, true); |
| 72 | + if (!is_array($env)) { |
| 73 | + fwrite(STDERR, "skip: index {$idx} is not valid JSON\n"); |
| 74 | + continue; |
| 75 | + } |
| 76 | + if (!empty($opts['repo']) && ($env['repo'] ?? '') !== $opts['repo']) { |
| 77 | + continue; |
| 78 | + } |
| 79 | + $entries[] = $env; |
| 80 | +} |
| 81 | + |
| 82 | +if (!empty($opts['json'])) { |
| 83 | + echo json_encode([ |
| 84 | + 'queue_depth' => $queueLen, |
| 85 | + 'total_enqueued' => $enqueued, |
| 86 | + 'shown' => count($entries), |
| 87 | + 'limit' => $limit, |
| 88 | + 'entries' => $entries, |
| 89 | + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"; |
| 90 | + exit(0); |
| 91 | +} |
| 92 | + |
| 93 | +printf("Queue: %s depth=%d total_enqueued=%d shown=%d/%d\n", |
| 94 | + CodeReviewQueue::QUEUE_KEY, $queueLen, $enqueued, count($entries), $limit); |
| 95 | +echo str_repeat('-', 100) . "\n"; |
| 96 | + |
| 97 | +if ($entries === []) { |
| 98 | + echo "(no entries)\n"; |
| 99 | + exit(0); |
| 100 | +} |
| 101 | + |
| 102 | +foreach ($entries as $i => $e) { |
| 103 | + $ts = (int)($e['ts'] ?? 0); |
| 104 | + $when = $ts > 0 ? date('Y-m-d H:i:s', $ts) : '?'; |
| 105 | + $age = $ts > 0 ? humanAge(time() - $ts) : '?'; |
| 106 | + $action = $e['action'] ?? '?'; |
| 107 | + $repo = $e['repo'] ?? '?'; |
| 108 | + $prNum = $e['pr_number'] ?? '?'; |
| 109 | + $head = $e['head_branch'] ?? '?'; |
| 110 | + $base = $e['base_branch'] ?? '?'; |
| 111 | + $author = $e['author'] ?? '?'; |
| 112 | + $sha = substr((string)($e['sha'] ?? ''), 0, 7); |
| 113 | + $retries = (int)($e['retry_count'] ?? 0); |
| 114 | + $id = $e['id'] ?? '?'; |
| 115 | + |
| 116 | + printf("[%2d] %s (%s ago)\n", $i, $when, $age); |
| 117 | + printf(" %s#%s %s %s -> %s by %s%s\n", |
| 118 | + $repo, $prNum, $action, $head, $base, $author, |
| 119 | + $sha !== '' ? " sha={$sha}" : ''); |
| 120 | + if ($retries > 0) { |
| 121 | + printf(" retry_count=%d\n", $retries); |
| 122 | + } |
| 123 | + if (!empty($opts['verbose'])) { |
| 124 | + printf(" id=%s\n", $id); |
| 125 | + if (!empty($e['pr_url'])) { |
| 126 | + printf(" %s\n", $e['pr_url']); |
| 127 | + } |
| 128 | + } |
| 129 | + echo "\n"; |
| 130 | +} |
| 131 | + |
| 132 | +// ---------- helpers ---------- |
| 133 | + |
| 134 | +function parseArgs(array $argv): array |
| 135 | +{ |
| 136 | + $o = []; |
| 137 | + for ($i = 1, $n = count($argv); $i < $n; $i++) { |
| 138 | + $a = $argv[$i]; |
| 139 | + switch ($a) { |
| 140 | + case '-h': |
| 141 | + case '--help': |
| 142 | + $o['help'] = true; |
| 143 | + break; |
| 144 | + case '--json': |
| 145 | + $o['json'] = true; |
| 146 | + break; |
| 147 | + case '--verbose': |
| 148 | + case '-v': |
| 149 | + $o['verbose'] = true; |
| 150 | + break; |
| 151 | + case '--metrics': |
| 152 | + $o['metrics'] = true; |
| 153 | + break; |
| 154 | + case '--limit': |
| 155 | + $o['limit'] = $argv[++$i] ?? ''; |
| 156 | + break; |
| 157 | + case '--repo': |
| 158 | + $o['repo'] = $argv[++$i] ?? ''; |
| 159 | + break; |
| 160 | + default: |
| 161 | + fwrite(STDERR, "unknown option: {$a}\n"); |
| 162 | + printUsage(); |
| 163 | + exit(1); |
| 164 | + } |
| 165 | + } |
| 166 | + return $o; |
| 167 | +} |
| 168 | + |
| 169 | +function printUsage(): void |
| 170 | +{ |
| 171 | + echo <<<TXT |
| 172 | +Usage: php scripts/github-code-review-list.php [options] |
| 173 | +
|
| 174 | +Peek (non-destructive) at the codereview:queue Redis list. |
| 175 | +
|
| 176 | +Options: |
| 177 | + --limit N Show at most N entries (default 50, newest first). |
| 178 | + --repo OWNER/REPO |
| 179 | + Filter to a single repository. |
| 180 | + --json Output the full envelopes as JSON (machine-readable). |
| 181 | + --metrics Print only queue depth + total_enqueued counter. |
| 182 | + -v, --verbose Include envelope id and PR URL in the human output. |
| 183 | + -h, --help Show this help. |
| 184 | +
|
| 185 | +Env: REDIS_HOST, REDIS_PORT (defaults match CodeReviewQueue). |
| 186 | +
|
| 187 | +TXT; |
| 188 | +} |
| 189 | + |
| 190 | +function humanAge(int $secs): string |
| 191 | +{ |
| 192 | + if ($secs < 60) return $secs . 's'; |
| 193 | + if ($secs < 3600) return intdiv($secs, 60) . 'm'; |
| 194 | + if ($secs < 86400) return intdiv($secs, 3600) . 'h'; |
| 195 | + return intdiv($secs, 86400) . 'd'; |
| 196 | +} |
0 commit comments