diff --git a/tests/mock_gh.sh b/tests/mock_gh.sh index 3114109..4b71641 100755 --- a/tests/mock_gh.sh +++ b/tests/mock_gh.sh @@ -3,16 +3,10 @@ # Mock gh CLI for unit tests. # Only direct children are queried now (no recursive updates of indirect children). -if [[ "$1" == "pr" && "$2" == "list" ]]; then - # Parse the --base argument to determine which PRs to return - base="" - for ((i=1; i<=$#; i++)); do - if [[ "${!i}" == "--base" ]]; then - next=$((i+1)) - base="${!next}" - fi - done - +if [[ "$1" == "api" && "$2" == repos/*"/pulls?base="* ]]; then + # Open PRs based on a branch (already --jq filtered to " "). + base="${2#*pulls\?base=}" + base="${base%%&*}" if [[ "$base" == "feature1" ]]; then # feature2 is a direct child of feature1 (PR #2) echo '2 feature2' diff --git a/tests/test_conflict_resolution_resume.sh b/tests/test_conflict_resolution_resume.sh index 415083c..4fd02b9 100644 --- a/tests/test_conflict_resolution_resume.sh +++ b/tests/test_conflict_resolution_resume.sh @@ -21,7 +21,7 @@ ok() { echo "✅ $1"; PASS=$((PASS+1)); } # Build a configurable gh mock in a temp dir. It records every invocation to # $CALLS and is driven by env vars set per scenario: # MOCK_LABELS newline-separated labels returned by `pr view --json labels` -# MOCK_COMMENTS_FILE file whose contents are returned by `pr view --json comments` +# MOCK_COMMENTS_FILE file served as the body of our own PR comments # The PR's base branch is not mocked: the script must take it from PR_BASE # (event payload), so a baseRefName query is an unhandled call and fails. make_mock_gh() { @@ -33,18 +33,18 @@ echo "gh $*" >> "$CALLS" if [[ "$1 $2" == "pr view" ]]; then case "$*" in *--json\ labels*) printf '%s\n' "${MOCK_LABELS:-}";; - *--json\ comments*) - # The comments file stands for our own comments only, so the query - # must restrict itself to those. - [[ "$*" == *viewerDidAuthor* ]] || { echo "comments query must filter by viewerDidAuthor" >&2; exit 1; } - cat "${MOCK_COMMENTS_FILE:-/dev/null}";; *) echo "unhandled pr view: $*" >&2; exit 1;; esac +elif [[ "$1 $2" == "api graphql" ]]; then + # The comments file stands for our own comments only, so the query must + # restrict itself to those. + [[ "$*" == *viewerDidAuthor* ]] || { echo "comments query must filter by viewerDidAuthor" >&2; exit 1; } + cat "${MOCK_COMMENTS_FILE:-/dev/null}" elif [[ "$1 $2" == "pr comment" ]]; then cat >/dev/null # consume the -F - body elif [[ "$1 $2" == "pr edit" ]]; then : -elif [[ "$1 $2" == "pr list" ]]; then +elif [[ "$1" == "api" ]]; then : # no sibling conflicts elif [[ "$1 $2" == "label create" ]]; then : @@ -93,6 +93,7 @@ setup_repo() { run_resume() { env ACTION_MODE=conflict-resolved PR_BRANCH=child PR_NUMBER=5 PR_BASE="$PR_BASE" \ + GITHUB_REPOSITORY=tester/repo \ GH="$MOCK_DIR/mock_gh.sh" GIT="$MOCK_DIR/mock_git.sh" \ MOCK_LABELS="$MOCK_LABELS" \ MOCK_COMMENTS_FILE="$MOCK_COMMENTS_FILE" CALLS="$CALLS" \ diff --git a/update-pr-stack.sh b/update-pr-stack.sh index e3fec22..02f8697 100755 --- a/update-pr-stack.sh +++ b/update-pr-stack.sh @@ -12,6 +12,7 @@ # PR_BRANCH - The head branch of the PR being resumed # PR_NUMBER - Its PR number, from the event payload # PR_BASE - Its base branch, from the event payload +# GITHUB_REPOSITORY - "owner/repo", provided by Actions # # Design note: # This script aims to output a transcript of "plain" git/gh commands that a @@ -44,8 +45,19 @@ format_state_marker() { read_state_marker() { local PR_NUMBER="$1" local BODIES - if ! BODIES=$(gh pr view "$PR_NUMBER" --json comments \ - --jq '.comments[] | select(.viewerDidAuthor) | .body'); then + if ! BODIES=$(gh api graphql --paginate \ + -F owner="${GITHUB_REPOSITORY%/*}" -F repo="${GITHUB_REPOSITORY#*/}" \ + -F number="$PR_NUMBER" -f query=' + query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + comments(first: 100, after: $endCursor) { + pageInfo { hasNextPage endCursor } + nodes { viewerDidAuthor body } + } + } + } + }' --jq '.data.repository.pullRequest.comments.nodes[] | select(.viewerDidAuthor) | .body'); then echo "Error: could not read comments of PR #$PR_NUMBER" >&2 exit 1 fi @@ -139,7 +151,8 @@ is_rebase_merge() { # Echoes " " for each open PR based on the merged branch. list_child_prs() { - log_cmd gh pr list --base "$MERGED_BRANCH" --json number,headRefName --jq '.[] | "\(.number) \(.headRefName)"' + log_cmd gh api "repos/{owner}/{repo}/pulls?base=$MERGED_BRANCH&state=open&per_page=100" \ + --paginate --jq '.[] | "\(.number) \(.head.ref)"' } # Args: head branch, base branch, PR number. git commands use the branch; gh @@ -255,7 +268,8 @@ has_sibling_conflicts() { # Find all open PRs with the conflict label that are based on BASE_BRANCH local CONFLICTED_SIBLINGS - CONFLICTED_SIBLINGS=$(gh pr list --base "$BASE_BRANCH" --label "$CONFLICT_LABEL" --json headRefName --jq '.[].headRefName' 2>/dev/null || echo "") + CONFLICTED_SIBLINGS=$(gh api "repos/{owner}/{repo}/pulls?base=$BASE_BRANCH&state=open&per_page=100" \ + --paginate --jq ".[] | select(any(.labels[]; .name == \"$CONFLICT_LABEL\")) | .head.ref" 2>/dev/null || echo "") for SIBLING in $CONFLICTED_SIBLINGS; do if [[ "$SIBLING" != "$EXCLUDE_BRANCH" ]]; then @@ -281,6 +295,7 @@ continue_after_resolution() { check_env_var "PR_BRANCH" check_env_var "PR_NUMBER" check_env_var "PR_BASE" + check_env_var "GITHUB_REPOSITORY" echo "Checking if PR #$PR_NUMBER ($PR_BRANCH) needs continuation after conflict resolution..."