name: batch-deps-upgrade description: Batch all open Dependabot dependency upgrade PRs into a single PR disable-model-invocation: true
Batch all open Dependabot dependency upgrade PRs into a single PR for this repository.
Step 1: Discover
Run: gh pr list --repo XRPLF/xrpl-py --label dependencies --state open --limit 500 --json number,title,headRefName,body,url
Parse each PR to extract package names and versions. Dependabot PRs come in two formats:
- Single-package PRs: title is
bump <pkg> from <old> to <new>— parse from title - Grouped PRs: title is
bump <pkg1> and <pkg2>with no versions — parse from PR body, which contains a structured list of package updates with version ranges
If any PR can't be parsed from either title or body, flag it for manual review. Build a table of all proposed upgrades. Report the table to the user before proceeding.
Step 2: Apply all upgrades
- Create a branch from main:
deps/batch-deps-upgrade-YYYY-QN(use current year and quarter) - Check for dependency conflicts before upgrading. For each proposed upgrade, review
pyproject.tomlconstraints and runpoetry show <pkg>to check if any other dependency pins a version range that would block the upgrade. Mark conflicts as Skipped (dependency conflict:) and do not attempt them. - For each remaining dependency, apply the upgrade:
- Direct deps (listed in
pyproject.tomlunder[tool.poetry.dependencies]or[tool.poetry.group.dev.dependencies]): update the version constraint inpyproject.tomlto the new version using caret (^<new_version>), then runpoetry update <pkg>. Always updatepyproject.tomlfor direct deps — even if the current constraint already allows the new version — so the pinned minimum stays current. - Transitive deps (not in
pyproject.toml): runpoetry update <pkg>to update within the existing constraint range
- Direct deps (listed in
- After all upgrades are applied, run
poetry lockto regeneratepoetry.lock. Do NOT deletepoetry.lockand regenerate from scratch. - Run
poetry installto sync the virtual environment. - Diff
pyproject.tomlandpoetry.lockagainst main to classify each Dependabot PR as:- Upgraded: version changed
- No-op: version was already current or newer
- If any upgrade changes the public API of the library (new errors, changed return types, removed functionality) and results in a breaking change, add an entry under
## [Unreleased]inCHANGELOG.md. - Verify completeness: every PR from Step 1 must have a status (Upgraded, No-op, or Skipped). If any PR is unaccounted for, stop and report it before proceeding.
Step 3: Validate
Run the full validation suite across all Python versions from the CI matrix. Repeat until everything passes.
Determine Python versions
Read each workflow file under .github/workflows/ to determine the Python versions used:
.github/workflows/unit_test.yml— theunit-testjob uses a matrix of Python versions; thelint-and-type-checkjob uses a single Python version (not a matrix).github/workflows/integration_test.yml— theintegration-testjob uses a matrix of Python versions.github/workflows/faucet_test.yml— thefaucet-testjob uses a matrix of Python versions
Extract the exact Python versions from each workflow's matrix.python-version array (or the PYTHON_VERSION env var for lint). These versions are the source of truth for validation.
Switching between Python versions
To switch Python versions for testing, use pyenv and poetry:
pyenv install <version> # install if not already present
pyenv local <version> # set the local Python version
poetry env use python<version> # point poetry to the correct interpreter
poetry install # reinstall deps for this interpreter
Replace <version> with the target version (e.g. 3.10, 3.11, 3.12, 3.13, 3.14). After running all tests for one version, repeat these steps to switch to the next.
Validation order
Run validation in parallel across all Python versions from the unit test matrix to speed things up. For each Python version, create a separate working directory (e.g. using git worktree or by spawning parallel agents) so that each version's virtual environment does not interfere with the others.
For each Python version, run the following in order:
Lint and type-check (only on the single lint Python version from the
lint-and-type-checkjob):poetry run poe lint poetry run mypy --strict --implicit-reexport xrplUnit tests:
poetry run poe test_unit poetry run coverage report --fail-under=85Integration tests (requires a single shared xrpld Docker container — start it once before running integration tests for any Python version):
- Pre-run cleanup:
docker rm -f xrpld-service 2>/dev/null || true - Start the container:
docker run \ --detach \ --publish 5005:5005 \ --publish 6006:6006 \ --volume "$PWD/.ci-config/:/etc/xrpld/" \ --name xrpld-service \ rippleci/xrpld:develop --standalone - Wait for port 6006 with a bounded timeout:
SECONDS=0 until nc -z localhost 6006 || [ $SECONDS -gt 120 ]; do sleep 2; done if ! nc -z localhost 6006; then echo "Error: xrpld did not start within 120s" docker logs xrpld-service exit 1 fi - Run for each Python version:
poetry run poe test_integration poetry run coverage report --fail-under=70 - Stop container after all versions complete:
docker logs xrpld-service && docker stop xrpld-service
- Pre-run cleanup:
Faucet tests:
poetry run poe test_faucet
Collect results from all parallel runs. All Python versions must pass.
Handling failures
If any step fails, attempt to fix the breaking change with code modifications before rolling back. Common patterns:
- Type annotation changes: newer versions of type stubs or mypy may require updated annotations. Fix the annotations.
- Deprecated API removals: if an upgraded dependency removes a previously deprecated function, update calls to use the replacement API.
- Import path changes: some packages reorganize their module structure on major bumps. Update import statements.
- Test compatibility: if a test utility changes behavior (e.g., aiounittest, coverage), update test configuration or code accordingly.
Only roll back and mark as Skipped if:
- The fix requires a large-scale migration across the codebase
- The upgrade is blocked by an external dependency constraint you cannot update
If a failure is traced to a specific dependency upgrade, revert that upgrade in pyproject.toml, re-run poetry lock && poetry install, mark it as Skipped, and re-run validation until green.
Step 4: Generate Outputs
After all upgrades are applied and validation passes, generate the following outputs:
4a. Code changes note
Write .claude/skills/batch-deps-upgrade/code-changes.md documenting every non-pyproject.toml source code change, explaining what broke, why, and the minimal fix applied.
4b. PR description
Write .claude/skills/batch-deps-upgrade/pr-description.md following the repo's PR template (.github/pull_request_template.md):
- For "High Level Overview of Change", summarize the batch upgrade.
- For "Context of Change", explain that this batches Dependabot PRs to reduce merge noise.
- For "Type of Change", determine dynamically:
- Check "Breaking change" ONLY if any upgrade visibly changes the library's public API (e.g., error messages, return types, removed functions). This aligns with whether a
CHANGELOG.mdentry was added in Step 2.7. - Otherwise, do not check any Type of Change — dependency upgrades are maintenance and don't fit "Refactor" (which means restructuring code without behavior change). Note in the PR body that the upgrade is maintenance.
- Check "Breaking change" ONLY if any upgrade visibly changes the library's public API (e.g., error messages, return types, removed functions). This aligns with whether a
- For "Did you update CHANGELOG.md?", check "Yes" if an entry was added, otherwise check "No, this change does not impact library users".
- Include a "Superseded Dependabot PRs" section with a table: PR (linked), Package, From, To, Status, MajorVersionUpgrade
- Status values: Upgraded, No-op (reason), Skipped (dependency conflict / CI failure: error)
- MajorVersionUpgrade:
Noif the major version number did not change. OtherwiseYesplus a link for each major version crossed. For example, 1.x → 3.x yieldsYes ([v2](url), [v3](url)). Each link should point to the package's release notes or changelog for that major version. Verify each link returns HTTP 200 and has meaningful content (e.g.,curl -sL -o /dev/null -w "%{http_code}" <url>); if a package doesn't publish per-version GitHub releases, fall back to the CHANGELOG file or the closest valid release tag.
- For every major version upgrade, add a "Major version upgrade notes" section below the table. For each major-version package, include:
- A link to the release notes
- A brief summary of key changes (breaking changes, deprecations, new features)
- An explanation of why no code changes were required, OR a summary of the code changes that were made. This helps reviewers understand the impact without having to read the full release notes themselves.
- Closing instructions with two paragraphs:
- "After merging, close the following superseded PRs (Skipped ones remain open for future handling): #X, #Y, #Z" — list only Upgraded and No-op PRs.
- "The following PRs were Skipped and should remain open: #A (package-a), #B (package-b), ..." — annotate each with the package name. These stay open so Dependabot keeps rebasing them.