# Upgrade Axum and Cargo Dependencies

Yes. An agent upgrades an Axum API's Cargo dependencies in groups, ports the code across Axum and SQLx breaking releases, and opens one pull request.

[Run this on Rush](https://cloud.getrush.ai/run/upgrade-cargo-dependencies/axum/), in a Project that has your repository.

## What the agent does

The agent reads `Cargo.toml` and `Cargo.lock` to see how far behind the project is and what moves together. In launchbadge/realworld-axum-sqlx the manifest asks for `axum` 0.3.4, `sqlx` 0.5 with `runtime-tokio-native-tls`, `postgres`, `uuid` and `time`, `tokio` 1.14, `tower` 0.4 and `tower-http` 0.2 with `trace`, `clap` at `3.0.0-beta.5`, `time` 0.2, `uuid` 0.8, `argon2` 0.3, `jwt` 0.15 with `hmac` 0.11 and `sha2` 0.9, `rand` 0.8, `itertools` 0.10, `thiserror` 1, `anyhow`, `async-trait`, `dotenv`, `env_logger` 0.9 and `log`. The lock has not moved since the code was written: `sqlx` resolves to 0.5.9, `time` to 0.2.27, `uuid` to 0.8.2. There are no tests under `tests/` and no `.sqlx` offline data, and the handlers use `sqlx::query!` and `query_as!` throughout, so even `cargo build` needs a `DATABASE_URL` pointing at a Postgres with the four files in `migrations/` applied.

Then it reads what a build proves here. `.github/workflows/rust.yml` starts a `postgres` service, installs `sqlx-cli`, runs `sqlx db setup`, then `cargo build --verbose` and `cargo test --verbose`, and finally starts the server with `cargo run` and runs the RealWorld Postman collection from a pinned fork against port 8080. `.env.sample` names the two settings `config.rs` reads through `clap`, `DATABASE_URL` and `HMAC_KEY`, plus `RUST_LOG`. The agent does the same in its sandbox: starts Postgres, writes `.env`, runs `sqlx db setup` so the macros can check every query at compile time, and keeps the Postman run as the integration check since there is no unit suite to lean on.

The groups are decided by where the code breaks. Axum and Tower move together, and the port is in `src/http/mod.rs` and `src/http/extractor.rs`: `AddExtensionLayer` becomes `Extension` as a layer, `axum::Server::bind` becomes a `TcpListener` with `axum::serve`, the `#[async_trait] impl FromRequest for AuthUser` and `MaybeAuthUser` become `FromRequestParts` without the macro, and route paths such as `/api/articles/:slug` in `src/http/articles/mod.rs` take the newer brace syntax. SQLx moves on its own, with the runtime feature split into `runtime-tokio` and `tls-native-tls`, and `Timestamptz` in `src/http/types.rs` rechecked as a `sqlx::Type` over `OffsetDateTime`. The `time` major changes `types.rs`, where `Format::Rfc3339` and `lazy_format` give way to the well-known format description, and `extractor.rs`, which builds a two-week `Duration`. `clap` from the beta to a release changes the attributes on `Config` in `src/config.rs` and needs the `derive` and `env` features. `hmac`, `sha2` and `jwt` move together because `extractor.rs` signs and verifies with all three and `NewMac` was renamed. `argon2`, `uuid`, `rand`, `itertools`, `thiserror` and `env_logger` are each a small group, and `dotenv` is swapped for its maintained fork only if the prompt allows a crate change.

After each group the agent runs `cargo build`, `cargo clippy`, `cargo test` and the Postman collection against a running server, all against the same Postgres. A group that passes is a commit with the port in it; a group that fails, for example a `jwt` release that changes how claims deserialize, is reverted with the compiler output kept. The pull request lists each bump with old and new versions, the source files each port touched, the skips with their reason, and the four check results.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Upgrade this Axum API's Cargo dependencies in groups: axum with tower and tower-http, then sqlx, then time, then clap, then hmac, sha2 and jwt together, then each remaining crate on its own. Start Postgres in the sandbox, write .env from .env.sample, install sqlx-cli and run sqlx db setup so the query macros compile. Axum and SQLx have had breaking releases since this code was written; port the code to them in the same commit as the bump, touching only the files the compiler points at. After each group run cargo build, cargo clippy, cargo test, and the RealWorld Postman collection against cargo run on port 8080. Keep a group only if everything passes; otherwise revert it and record the error. Open one pull request with one commit per group, one line per bump with old and new versions, the files each port touched, and every upgrade left out with the reason.",
  "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 that changes `Cargo.toml` and `Cargo.lock` in one commit per group that passed, each commit carrying the port that group needed: `src/http/mod.rs` and `src/http/extractor.rs` for Axum, `src/http/types.rs` for `time` and SQLx, `src/config.rs` for `clap`, and the route strings in `src/http/articles/mod.rs` and its siblings. Migrations are untouched.

The description is one line per bump with old and new versions, the files each port touched, a list of upgrades left out with the compiler error or failing Postman request that excluded them, and the result lines of `cargo build`, `cargo clippy`, `cargo test` and the collection run. The trajectory keeps the full output for every group, including the reverted ones, and the server log from each collection run.

Before merging, a reviewer reads the `extractor.rs` port closely, since it is the token verification path, checks that the `Timestamptz` serialization still produces RFC 3339 strings, and confirms the Postman run exercised login and article creation, because there is no unit test covering either.

## 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`). After the first run catches the project up, each week's run opens one pull request with that week's bumps, or none when nothing moved. Because each group is its own commit, dropping a bump before merge is a revert of one commit.

To have the link reach your server, start the weekly run from your own scheduler instead, with `POST /o/{org}/sessions` and an API key that has a webhook URL: Rush then POSTs `run.completed` with the `session_url` when the turn ends, and a handler on your side can post the pull request link where the API's maintainers read. A run that ends with no safe group opens no pull request and says why in its result.

## Why run it on Rush?

An Axum API that has not moved in a few releases cannot be upgraded by a bot: every bump is also a port, the query macros need a live database to compile, and the only integration check is a Postman collection against a running server. On Rush the agent has Postgres, `sqlx-cli` and the server in its own sandbox, does one group at a time, keeps the groups that pass and the compiler output for the ones that do not, so the pull request that reaches you is already green.

The record shows every build, every clippy run and every collection run, which is the evidence a reviewer needs when the code that changed is the authentication extractor. The same request runs against every Axum service you maintain at once.

## Frequently asked questions

### There are no tests. What stops a bad port?

The compiler, `clippy`, and the RealWorld Postman collection the repository's own CI runs against a live server. The `sqlx::query!` macros also check every query against the database at compile time. A behavior the collection does not cover is named in the description as unverified.

### Why does cargo build need Postgres?

Because the handlers use SQLx's checked macros and the repository has no `.sqlx` offline data. The macros connect to `DATABASE_URL` during compilation and fail without the schema, so the agent runs `sqlx db setup` before the first build. It can also generate offline data with `cargo sqlx prepare` if you ask for it in the prompt.

### Will it change the route syntax?

Yes, as part of the Axum group, since newer releases use braces for path parameters where this code has colons. The change is in the router strings only, named in the description, and the Postman run confirms the routes still answer.

### Does it replace the dotenv crate?

Only if the prompt allows a crate swap; by default it bumps what is there and notes that `dotenv` has a maintained fork. The one-line change in `main.rs` is easy to review if you ask for it.

### Can it do only part of the catch-up?

Yes. Name the groups to take, for example Axum and SQLx only, and the rest stays at its locked version with a line in the pull request saying so. The next weekly run picks up from there.
