All jobs

Upgrade Cargo Dependencies in a Rust CLI

Yes. An agent upgrades a Rust CLI's Cargo dependencies in groups, runs the tests, clippy and the MSRV check after each, and opens one PR with the skips explained.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads Cargo.toml and Cargo.lock to learn what is pinned and what floats. In casey/just the package is just 1.59.0, edition 2024, rust-version 1.89.0, in a workspace with crates/action-versions and crates/generate-book; Cargo.lock resolves 155 packages. Nearly every dependency is a caret range on an old floor, clap 4.0.0, serde 1.0.130, regex 1.10.4, snafu 0.9.0, similar 3.0.0, tempfile 3.0.0, which the lock has already carried forward to clap 4.6.7, serde 1.0.229, regex 1.13.1, similar 3.2.0 and tempfile 3.27.0. One crate is pinned exactly: clap_complete = "=4.6.11" with the unstable-dynamic feature, and the changelog records why (Pin clap_complete to last compatible version, #2800). pulldown-cmark appears twice in the lock, 0.13.4 for the main crate's src/tangle.rs and 0.9.6 for generate-book, which asks for 0.9.1 and still declares edition 2018. There is no Dependabot or Renovate configuration under .github/, so the lock moves only when someone runs cargo update.

Then it reads what a green build proves. .github/workflows/ci.yaml sets RUSTFLAGS: --deny warnings and runs four jobs: lint with cargo clippy --all --all-targets, cargo fmt --all -- --check, ./bin/forbid (a ripgrep for dbg! and todo outside the changelog) and shellcheck www/install.sh; msrv with cargo check on 1.89.0; pages, which builds the book with cargo run --package generate-book and mdbook 0.4.52; and test with cargo test --all on Ubuntu, macOS and Windows plus a run of www/install.sh. Cargo.toml denies clippy all and pedantic and the unreachable_pub lint, so a new deprecation warning from an upgraded crate is a failed build. The tests are one harness: autotests = false and a single [[test]] named integration at tests/lib.rs, which pulls in 118 modules through a Test builder in tests/test.rs that writes a justfile to a tempdir, runs the built binary and compares stdout and stderr with pretty_assertions. tests/completions.rs checks --completions for bash, elvish, fish, nushell, powershell and zsh, and tests/man.rs checks --man prints a .TH just 1 header, so the clap family is tested through its output.

The groups follow those facts. cargo update first, which moves transitive crates and any direct crate still inside its range, then one group at a time for crates with a newer minor or major than the range allows: the clap family (clap, clap_complete with its pin raised only if the completion and man tests pass, clap_mangen) together because src/main.rs uses CompleteEnv and src/lib.rs uses ArgValueCompleter and PathCompleter from the dynamic completion engine; pulldown-cmark for the main crate alone, since generate-book pairs its 0.9 with pulldown-cmark-to-cmark 10; then snafu, strum, similar, semver, nix, which and the rest each on their own. After each group the agent runs cargo test --all, cargo clippy --all --all-targets with --deny warnings, cargo fmt --all -- --check and cargo check on the 1.89.0 toolchain, and for the clap group also cargo run -- --completions zsh and --man by eye in the transcript, since those outputs are what the tests compare.

A group that passes is a commit with the manifest change and the lock; a group that fails, such as a clap_complete release that changes the dynamic completion output the tests match, is reverted with the first error kept. The agent bumps the floor in Cargo.toml only when the new version is needed to compile, and otherwise leaves the range and moves the lock, which is what the justfile's ci entry expects with cargo update --locked --package just. The pull request lists one line per bump with old and new versions, the crates left out with their error, and the four check results.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Upgrade this Rust CLI's Cargo dependencies. Read Cargo.toml, Cargo.lock and .github/workflows/ci.yaml first; in a crate shaped like casey/just, use RUSTFLAGS=--deny warnings and the rust-version in Cargo.toml for an MSRV check. Run cargo update first and keep it as its own commit if the tests pass. Then upgrade in groups the crates a newer minor or major exists for: the clap family (clap, clap_complete, clap_mangen) together, raising an exact pin only if the completion and man tests still pass; pulldown-cmark for the main crate alone; then each remaining crate on its own. After each group run cargo test --all, cargo clippy --all --all-targets, cargo fmt --all -- --check and cargo check on the MSRV toolchain. Keep a group only if everything passes; otherwise revert it and record the first error. Raise a version floor in Cargo.toml only when the new version is required to compile. Open one pull request with one commit per group, one line per bump with old and new versions, each skipped crate with its error, and the check results.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on a new branch with one commit per group that passed: Cargo.lock in every commit, Cargo.toml only where a floor or the clap_complete pin had to move, and no source changes unless a group needed a one-line port, which its commit message names. The crates in crates/ keep their own manifests unless their group was taken.

The description is one line per bump with old and new versions, the groups reverted with the compiler, clippy or test error that excluded them, and the results of cargo test --all, clippy under --deny warnings, the fmt check and cargo check on 1.89.0. The transcript keeps the full output of every group, including the reverted ones, so a maintainer can see exactly which completion test a clap_complete release broke.

Before merging, a reviewer looks at the clap commit first, because tests/completions.rs and tests/man.rs compare generated text, then checks that www/install.sh and the book build were not affected, and that no floor was raised beyond what compiling required, since a higher floor is a change for everyone who builds from source.

Run it on every Monday morning

Create an automation in the Project with this prompt and a schedule trigger (cron 0 9 * * 1 in your timezone). The first run catches the lock up; each week after that opens one pull request with that week's bumps, or none when cargo update changes nothing and no crate has a new minor. Because each group is its own commit, dropping a bump before merge is a revert of one commit.

To run it from your own scheduler instead, send this body to POST /o/{org}/sessions once a week with an Idempotency-Key built from the repository name and the ISO week, and set a webhook URL on the API key: Rush POSTs run.completed with session_url when the turn ends, and your handler can assign the pull request to whoever owns dependencies that week. A crate that failed stays at its old version and is tried again the following week.

The use case this job follows, with its automation and trigger: Upgrade dependencies weekly.

Why run it on Rush?

A CLI with 118 integration test modules that compare exact output, a --deny warnings policy and a pinned completion crate cannot be upgraded by a bot that bumps everything at once: the red pull request says nothing about which crate broke which test. On Rush the agent takes one group at a time in its own sandbox, keeps the groups that pass and the error for the ones that do not, and the pull request that reaches you is already green on the same commands CI runs.

The record shows every test run, every clippy run and the MSRV check per group, which is the evidence a maintainer needs when the crate that moved is the one generating shell completions. The same request runs against every Rust CLI you maintain at once.

Frequently asked questions

Why run cargo update before touching Cargo.toml?

Because most floors here are old caret ranges, so the lock can move a long way with no manifest change. cargo update is the cheap first commit; the groups after it are only for crates whose newer release is outside the range, and the justfile's ci entry runs cargo update --locked --package just to confirm the lock is consistent.

Will it raise the clap_complete pin?

It tries, in the clap group, and keeps the change only if tests/completions.rs and tests/man.rs still pass, since the pin exists because a later release changed the dynamic completion behavior. If they fail, the pin stays and the description quotes the failing test.

Does it touch generate-book and action-versions?

Only in the cargo update commit, where their transitive crates move, unless you name them in the prompt. generate-book pairs pulldown-cmark 0.9 with pulldown-cmark-to-cmark 10, and moving one without the other does not compile, so the agent treats that pair as its own group when asked.

What about the MSRV?

The msrv job runs cargo check on 1.89.0, and a dependency release that needs a newer compiler fails there. The agent runs the same check after each group and reverts a group that raises the effective MSRV, listing the crate and the version that would require a rust-version bump.

Does it run the Windows and macOS matrix?

No. The sandbox is Linux, and the description says so. A crate that is only used on another platform, such as ctrlc under cfg(windows), is upgraded on the strength of cargo check and listed as not exercised by the tests.