# Upgrade Ruby on Rails Dependencies Every Week

Yes. A weekly request has an agent run bundle update, test each gem bump with bin/rails test, drop the ones that fail, and open one pull request.

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

## What the agent does

In a Rails app like rubygems/rubygems.org, the agent starts with the files Bundler reads. The Gemfile reads its Ruby from .ruby-version (4.0.6), pins rails at ~> 8.1.3 and declares about seventy runtime gems with pessimistic constraints such as puma ~> 8.0, good_job ~> 3.99 and searchkick ~> 6.1. Gemfile.lock records the resolved versions (rails 8.1.3.1, puma 8.0.2, pg 1.6.3) and was written by Bundler 4.0.16, the same version the Tests workflow installs through RUBYGEMS_VERSION. The agent installs that Bundler first so the lockfile it writes back has the same BUNDLED WITH line.

Before it changes anything, the agent records the pins a person put there on purpose. This Gemfile holds json below 3 with a comment explaining that json 3.0 dropped the positional options hash ActiveSupport::JSON.decode still uses, and it pins pp to 0.6.4. A weekly upgrade leaves those alone and repeats the reason in the pull request. The avo-advanced gem sits in an optional group behind a private packager.dev source that needs a BUNDLE_PACKAGER__DEV token; without it the agent installs with that group left out, the way the repository's own fork CI does, and says the Avo Pro gems were not checked.

The test suite needs services. mise.toml and docker-compose.yml describe Postgres 18.6, memcached and OpenSearch 2.13.0, and test_helper.rb loads Minitest with mocha, shoulda and Capybara. The agent brings those up, runs bin/rails test on the current lockfile to get a baseline, then runs bundle update within the Gemfile's constraints. If the suite goes red, it bisects: it restores the old lockfile, updates gems one at a time or in small groups with bundle update --conservative, and finds which bump causes the failure.

Each gem whose new version breaks the suite goes back to its locked version, and the agent keeps the failing test name and the first lines of the error. The gems that pass stay updated. It runs bin/rails test once more on the final lockfile so the pull request reflects one green state, not a collection of separately green experiments. System tests under test/system need Playwright browsers, so the agent runs them only when it can install them and reports which suites it ran.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Upgrade this Rails app's Bundler dependencies to the latest versions the Gemfile constraints allow. Install the Bundler version named under BUNDLED WITH in Gemfile.lock, start Postgres, memcached and OpenSearch from docker-compose.yml, and run bin/rails test before changing anything to record a baseline. Run bundle update, then bin/rails test. For each gem whose new version fails a test that passed in the baseline, return it to its locked version with bundle update --conservative and record the failing test and error. Keep Gemfile pins that carry a comment, such as json < 3, and mention them. If the private Avo source cannot be reached, install without that optional group and say so. Run bin/rails test on the final lockfile, then open one pull request with a table of every gem bump (old and new version), a list of each skipped gem with the error that blocked it, and the exact commands you ran.",
  "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

The pull request changes Gemfile.lock and, only where a constraint blocks a release the suite accepts, the Gemfile. Its description has a table of gems with the old and new version, a section of gems that stayed behind with the test that failed and the error text, and the pins that were left on purpose with the reason quoted from the Gemfile comment. Baseline failures, tests that were already red before any update, are listed separately so nobody blames a bump for them.

A reviewer reads the table for major version jumps in gems the app talks to directly (rails, puma, good_job, shoryuken, opensearch-ruby), checks the CHANGELOG link for each of those, and confirms the Tests workflow on the pull request runs the General, System and Avo matrix the repository defines. The session's transcript, read with GET /o/{org}/sessions/{id}/messages, holds every bin/rails test run with its output, so a skipped gem's failure can be checked against the real error instead of a summary.

## Run it on every Monday morning

Put the request above in a scheduled job you already run, such as a cron entry or a GitHub Actions workflow with a schedule trigger, and have it send POST /o/{org}/sessions once a week per Rails repository. Give each call an Idempotency-Key built from the repository and the week, so a retried job returns the run it already started instead of opening a second upgrade branch.

Set a webhook URL on the API key and your endpoint receives run.completed or run.failed when the turn ends. Read the session for the pull request link and post it where the team reviews dependency work. Because the agent leaves broken gems at their old versions, the next week's run tries them again against whatever new release has appeared.

## Why run it on Rush?

A Rails upgrade that is worth merging needs Postgres, memcached and OpenSearch running, a specific Bundler, and a test suite that takes real time. Doing that on a laptop ties up the machine and its local databases; doing it in a Rush sandbox gives the agent its own computer where it can start those services, run bin/rails test as many times as the bisection needs, and keep every command's output in the session record.

Dependabot can open a grouped bundler pull request too, and this repository already has one configured for minor and patch updates. What it cannot do is take the red gems back out. The agent's pull request arrives with the failing bumps already removed and each one explained, so the review is about the gems that changed, and several Rails apps can get their weekly run at the same time within the key's concurrency limit.

## Frequently asked questions

### Will it upgrade Rails itself across a minor version?

Only as far as the Gemfile allows. With rails ~> 8.1.3 the agent can move to a newer 8.1 patch release but not to 8.2. Widening that constraint is a decision for a person, so the agent lists a newer Rails minor as available instead of editing the requirement.

### What about gems from a private source?

The agent needs credentials for that source to resolve its gems. In rubygems.org the avo-advanced gem lives behind packager.dev in an optional group, so without the token the agent installs with that group excluded and says those gems were not updated. Save the token as a Project secret if you want them included.

### How does it decide which gem broke a test?

It compares against a baseline run on the current lockfile, so only failures that appear after the update count. Then it narrows the update down with bundle update --conservative on single gems or small groups until the failing test points at one gem.

### Does it run system tests?

The repository's system tests use Capybara with Playwright browsers. The agent runs them when it can install the browsers in its sandbox and reports which suites ran, so a pull request that only ran the general suite says so in its description.
