# Td Rebase Pr

> Check out a PR branch, rebase it onto the base branch, push, and wait for CI to complete. Reports whether the rebase alone fixed CI or if further action is needed. Use when a PR is stale and may just need a rebase to pass CI.

- Skill: `ansible/td-rebase-pr` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ansible/td-rebase-pr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ansible/td-rebase-pr/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ansible (https://skillmd.com/u/ansible)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ansible/td-rebase-pr

---


> **[Team DevTools]** Running `td-rebase-pr` — from [ansible/team-devtools](https://github.com/ansible/team-devtools/tree/main/.agents/skills/td-rebase-pr)

Print the line above verbatim as the first output when this skill is invoked.

# Rebase PR

Check out a PR branch, rebase it onto the base branch (usually `main`),
push, and poll CI until all jobs complete. Report the result.

This skill does **one thing** — rebase and report. It does not diagnose
or fix CI failures.

---

## Input

Required:
- **repo** — e.g., `ansible/vscode-ansible`
- **PR number** — e.g., `2716`

---

## Entry Gate

```bash
gh auth status
```

If not authenticated, stop.

Verify the PR exists and is open:

```bash
gh pr view PR_NUMBER --repo OWNER/REPO \
  --json state,headRefName,baseRefName \
  --jq '{state, head: .headRefName, base: .baseRefName}'
```

If the PR is not open, stop.

---

## Step 1 — Check out the PR branch

If the target repo is your current working directory:

```bash
gh pr checkout PR_NUMBER
```

Otherwise clone first:

```bash
gh repo clone OWNER/REPO /tmp/rebase-pr-REPO
cd /tmp/rebase-pr-REPO
gh pr checkout PR_NUMBER
```

---

## Step 2 — Check if rebase is needed

```bash
git fetch origin BASE_BRANCH
git log --oneline origin/BASE_BRANCH..HEAD | wc -l
git merge-base --is-ancestor origin/BASE_BRANCH HEAD && echo "UP_TO_DATE" || echo "BEHIND"
```

If `UP_TO_DATE`, the branch already includes all commits from the base
branch. No rebase or push is needed — no new CI run will be triggered.
Skip to Step 5 and report the existing CI results. Make it clear in the
output that these are **existing results, not from a new run**.

---

## Step 3 — Rebase

```bash
git rebase origin/BASE_BRANCH
```

**If rebase succeeds cleanly:**

Push the rebased branch:

```bash
source ~/.ansibuddy_env 2>/dev/null || true && git push --force-with-lease
```

If push is rejected, stop and report:

```
**Action taken:** push rejected (branch was modified remotely)
```

Do not attempt to pull and reconcile — the bot branch moved under you.
The orchestrator can decide to retry or skip.

If the PR head branch is on a fork (different owner than the repo),
`--force-with-lease` will fail because you lack push access to the fork.
Report that fork PRs cannot be rebased by this skill and stop.

**If rebase has conflicts:**

Count the conflicting files:

```bash
git diff --name-only --diff-filter=U
```

If 3 or fewer files conflict, attempt resolution:
- For lock files (`pnpm-lock.yaml`, `uv.lock`, `yarn.lock`): accept
  theirs and complete the rebase, then regenerate in Step 3a.
- For other files: abort and report that manual conflict resolution is
  needed.

```bash
git checkout --theirs pnpm-lock.yaml  # or uv.lock, yarn.lock
git add pnpm-lock.yaml
git rebase --continue
```

If more than 3 files conflict or non-lock-file conflicts exist:

```bash
git rebase --abort
```

Report that the rebase has conflicts requiring manual resolution and stop.

---

## Step 3a — Regenerate lockfiles (after conflict resolution)

**This step runs only when Step 3 resolved lockfile conflicts with
`--theirs`.** If the rebase completed cleanly (no conflicts), skip
straight to the push in Step 3.

### Detect toolchain

```text
if Taskfile.yml exists AND package.json exists -> TypeScript (pnpm)
elif tox.ini exists                            -> Python (uv)
elif pyproject.toml exists (no tox.ini)        -> Python (uv)
else                                           -> Unknown
```

If unknown, abort and report:

```
**Action taken:** lockfile conflict resolved but toolchain unknown — cannot regenerate. Closing PR so Renovate can recreate from current main.
```

Close the PR with a comment explaining why:

```bash
gh pr close PR_NUMBER --repo OWNER/REPO \
  --comment "Lockfile conflict could not be safely regenerated (unknown toolchain). Renovate will recreate this PR from current main."
```

Stop.

### Run regeneration

```bash
# TypeScript
pnpm install

# Python (tox or uv)
uv lock
```

If the regeneration command exits non-zero, abort and report:

```
**Action taken:** lockfile regeneration failed (COMMAND exited CODE). Closing PR so Renovate can recreate from current main.
```

Close the PR with a comment and stop.

### Verify gate

The regenerated lockfile must differ from what `--theirs` produced.
If it does not, the resolution was a no-op and the lockfile may carry
stale pins.

```bash
git diff --exit-code uv.lock  # or pnpm-lock.yaml
```

- If `git diff --exit-code` returns **1** (file changed): the
  regeneration produced a fresh lockfile. Stage and commit it:

  ```bash
  git add uv.lock  # or pnpm-lock.yaml
  git commit -m "chore: regenerate lockfile after rebase conflict resolution"
  ```

- If `git diff --exit-code` returns **0** (no change): the lockfile
  is identical to the `--theirs` version. This is acceptable only if
  the PR's lockfile changes were already consistent with main. Log a
  warning but proceed:

  ```
  **Warning:** lockfile unchanged after regeneration — --theirs resolution matched current state
  ```

### Push

```bash
source ~/.ansibuddy_env 2>/dev/null || true && git push --force-with-lease
```

If push is rejected, stop and report as described in Step 3.

---

## Step 4 — Poll CI until complete

Poll every 60 seconds until no jobs are `IN_PROGRESS` or `PENDING`.

If after 5 minutes **no checks have appeared at all** (CI never started),
report a timeout and stop — workflows may be disabled or misconfigured.

Once at least one check is running or completed, keep polling with no
time limit until all jobs finish. CI duration varies across repos — some
jobs take 5 minutes, others 30+.

```bash
elapsed=0
while true; do
  checks=$(gh pr checks PR_NUMBER --repo OWNER/REPO \
    --json name,state --jq '.[] | select(.state != "SKIPPED")')
  total=$(echo "$checks" | grep -c . || true)
  pending=$(echo "$checks" | grep -cE "IN_PROGRESS|PENDING|QUEUED" || true)

  # CI never started — timeout after 5 minutes
  if [ "$total" -eq 0 ] && [ "$elapsed" -ge 300 ]; then
    echo "TIMEOUT: no checks appeared after 5 minutes"
    break
  fi

  # All checks finished
  if [ "$total" -gt 0 ] && [ "$pending" -eq 0 ]; then
    break
  fi

  sleep 60
  elapsed=$((elapsed + 60))
done
```

If the loop exits due to timeout, report:

```
**CI run:** timed out (no checks appeared after 5 minutes)
```

---

## Step 5 — Report result

After CI completes, check for code failures (applying the same skip list
as `td-scan-bot-prs`):

```bash
gh pr checks PR_NUMBER --repo OWNER/REPO \
  --json name,state --jq '.[] | select(.state == "FAILURE" or .state == "ERROR") | .name'
```

**Skip list** (not code failures):
- `codecov/project`, `codecov/patch`
- `docs/readthedocs.org:*`
- `ack / ack`
- `renovate/stability-days`, `renovate/artifacts`
- `SonarCloud Code Analysis`

### Output format

```
## Rebase Result

**Repo:** OWNER/REPO
**PR:** #NUMBER — TITLE
**Action taken:** rebased onto BASE_BRANCH / rebased onto BASE_BRANCH (lockfile regenerated) / already up to date / conflicts (aborted) / lockfile regen failed (PR closed)
**CI run:** new (triggered by push) / existing (no push, reporting old results)
**CI result:** all passing / N code failures

### Failing checks (if any)
- check_name_1
- check_name_2
```

If all code checks pass: report success. The rebase fixed it.

If code checks still fail: report the failing check names. The
orchestrator will pass these to `td-diagnose-ci`.

