#!/usr/bin/env bash # Prints a PR's reviews, and optionally waits for one against the current head. # Exists because twenty PRs merged with their reviews unread; see # context/code/pr-review.md. set -euo pipefail usage() { cat >&2 <<'EOF' usage: pr-reviews [--wait ] [--interval ] --wait wait up to this long for a review whose commit is the current head (default 0 = print what exists and exit) --interval seconds between polls (default 30) exit 0 a review against the current head exists (or --wait was 0) exit 1 --wait elapsed with no review against the current head exit 4 usage or environment error DO NOT PIPE THIS INTO head/tail. `$?` is then the pager's status, not this script's, and a --wait that timed out reads as success. That trap has cost this repo four separate diagnoses. Redirect to a file and read it instead. A review may target an EARLIER head, which is why the head is compared rather than the count: a finding against an old commit may be fixed or still live, and only reading it tells you which. EOF exit 4 } PR=""; WAIT=0; INTERVAL=30 while [ $# -gt 0 ]; do case "$1" in --wait) WAIT="${2:-}"; shift 2 ;; --interval) INTERVAL="${2:-}"; shift 2 ;; -h|--help) usage ;; *) [ -z "$PR" ] || usage; PR="$1"; shift ;; esac done [ -n "$PR" ] || usage # `gh` is shell-aliased to a different tool in this repo's shells, so the name # is never trusted: a real gh is located explicitly. gh_() { if command -v gh >/dev/null 2>&1 && gh --version 2>/dev/null | grep -q '^gh version'; then gh "$@" else nix shell nixpkgs#gh --command gh "$@" fi } fetch() { gh_ pr view "$PR" --json headRefOid,reviews; } report() { local json="$1" head head=$(printf '%s' "$json" | jq -r '.headRefOid') printf 'pr-reviews: #%s head=%s\n' "$PR" "${head:0:8}" printf '%s' "$json" | jq -r --arg head "$head" ' if (.reviews | length) == 0 then " (no reviews)" else .reviews[] | " [\(.state)] \(.submittedAt) commit=\(.commit.oid[0:8])" + (if .commit.oid == $head then " <- CURRENT HEAD" else " <- an EARLIER head; verify whether it still applies" end) + "\n" + (.body | split("\n") | map(" " + .) | join("\n")) end' } has_head_review() { printf '%s' "$1" | jq -e --arg h "$(printf '%s' "$1" | jq -r .headRefOid)" \ 'any(.reviews[]?; .commit.oid == $h)' >/dev/null 2>&1 } json=$(fetch) report "$json" [ "$WAIT" -gt 0 ] || exit 0 if has_head_review "$json"; then exit 0; fi deadline=$(( $(date +%s) + WAIT )) while [ "$(date +%s)" -lt "$deadline" ]; do sleep "$INTERVAL" json=$(fetch) if has_head_review "$json"; then printf '\npr-reviews: a review against the current head arrived.\n\n' report "$json" exit 0 fi done printf '\npr-reviews: waited %ss; no review against the current head.\n' "$WAIT" printf 'Reviews arrive as the owner quota allows, so absence is not approval.\n' exit 1