Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions plugins/tend-ci-runner/skills/nightly/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,39 @@ a one-line reason) plus a `_Last refreshed: <YYYY-MM-DD>_` footer. Updates:

Find conflicted PRs from this bot and from upstream dependency bots:

Don't filter `gh pr list --json mergeable` on `== "CONFLICTING"`. `mergeable` is computed lazily: the first query after `main` moves returns `UNKNOWN` and only *enqueues* the computation, so a cold read reports a conflicted PR as clean. There is no blocking read — [the REST docs](https://docs.github.com/en/rest/pulls/pulls#get-a-pull-request) prescribe resubmitting the request until the value settles. Test-merge locally instead: `git merge-tree` answers the same question synchronously, in-process, with no retry loop.

```bash
BOT_LOGIN=$(gh api user --jq '.login')
git fetch --quiet origin main
for author in "$BOT_LOGIN" app/dependabot app/renovate; do
gh pr list --author "$author" --json number,title,mergeable,headRefName,author \
--jq '.[] | select(.mergeable == "CONFLICTING")'
out="/tmp/prs-${author//\//-}.json" # `app/dependabot` has a slash; strip it
# `--limit 100` is load-bearing: `gh pr list` defaults to 30 and truncates
# silently, so PR 31 onward would never be test-merged.
gh pr list --author "$author" --limit 100 --json number,title,headRefName,author > "$out"
# A failed query leaves `$out` empty, which reads as "no conflicts" — the
# same silent-miss shape. `[]` (no open PRs) is 3 bytes, a failure is 0.
[ -s "$out" ] || { echo "query for $author never landed — conflicts unverified"; continue; }
# One fetch for every head, forced because bot branches get force-pushed.
# `refs/pull/N/head` also covers PRs opened from a fork.
mapfile -t refs < <(jq -r '.[].number | "refs/pull/\(.)/head:refs/tend/pr/\(.)"' "$out")
[ "${#refs[@]}" -eq 0 ] || git fetch --quiet --force origin "${refs[@]}"
jq -r '.[] | "\(.number)\t\(.title)"' "$out" | while IFS=$'\t' read -r n title; do
# Clean merge: exit 0. Conflict: non-zero with the tree OID and conflicted
# paths on stdout. Anything else (ref never fetched, bad revision): non-zero
# with stdout empty — so test `$tree`, not the exit status alone.
if tree=$(git merge-tree --write-tree origin/main "refs/tend/pr/$n" 2>/dev/null); then
continue
elif [ -n "$tree" ]; then
echo "CONFLICTING: $author #$n $title"
else
echo "merge test never ran, conflicts unverified: $author #$n $title"
fi
done
done
```

Skip the rest of this step if none of the queries return anything.
Skip the rest of this step only when every query landed and nothing printed. A PR whose merge test never ran is not a clean one — investigate it and report it as unverified rather than counting it clean.

### Upstream dependency bots: trigger the bot's own rebase

Expand Down
Loading