← Back to list

Automating Ruby Version Upgrades

Simplify Ruby version upgrades — here’s your guide!

Luis Fernández in seQura-tech · 2025-02-18 10:48 · 6 claps · 5.7 min read
#development #security #upgrade #automation #rails
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Automating Ruby Version Upgrades

Photo by Joshua Fuller on Unsplash

Photo by Joshua Fuller on Unsplash

Context and Motivation

Keeping Ruby up-to-date is essential for security, performance, and stability. But manually upgrading versions across multiple projects can be tedious and error-prone.

With Updatecli and GitHub Actions, we can automate this process, ensuring projects always run on the latest stable Ruby version. This is especially important for organizations with strict security and performance standards, where outdated software can introduce risks.

Scope

In this article, we aim to automate the Ruby version upgrade process in a project repository using Updatecli and GitHub Actions. Many Ruby projects define the Ruby version in multiple files, such as:

  • Gemfile & Gemfile.lock - Which are the files that define the ruby dependencies to be installed by bundler
  • .ruby-version which is useful for rbenv tool.
  • .env - This could be handy to standardize the ruby version to be used across different systems such as Docker-Compose, Continuous Integration Builds or Production Environment.

This guide shows how to set up an automated workflow to detect new Ruby versions, update project files, and create pull requests — without manual effort.

To follow along, you can explore the demo repository where you’ll find the full configuration, GitHub Actions workflows, and example pull requests showcasing the automation in action:

[embed]GitHub - treezio/ruby-version-upgrade-updatecli: Demo repository to showcase ruby version upgrade… Demo repository to showcase ruby version upgrade process using updatecli - treezio/ruby-version-upgrade-updatecligithub.com

How it works

Updatecli is a declarative dependency management tool.

I’ve already written an extended article featuring updatecli explaining how we keep all third party tooling up-to-date at seQura.

Using https://github.com/ruby/ruby as an example, Updatecli follows three key steps:

  1. Fetch the latest Ruby version: It retrieves the newest available Ruby release (or any other version based on filters).
  2. Update repository files — It applies the new Ruby version to relevant files.
  3. Create a pull request — It proposes the necessary changes in a structured PR.

Updatecli Pipeline Configuration

To achieve this, we define an **Updatecli pipeline configuration**:

---
name: Bump Ruby Version

pipelineid: "ruby-version"

scms:
  default:
    kind: github
    spec:
      user: "{{ .github.user }}"
      email: "{{ .github.email }}"
      owner: "{{ .github.owner }}"
      repository: "{{ .github.repository }}"
      token: "{{ requiredEnv .github.token }}"
      username: "{{ .github.username }}"
      branch: "{{ .github.branch }}"

sources:
  RubylatestVersion:
    kind: githubrelease
    name: Get the latest Ruby version
    spec:
      owner: ruby
      repository: ruby
      token: "{{ requiredEnv .github.token }}"
      username: "{{ .github.username }}"
      typefilter:
        latest: true
    transformers:
    - trimprefix: "v"
    - replacer:
        from: "_"
        to: "."

targets:
  rubVersionDockerTag:
    kind: file
    sourceid: RubylatestVersion
    name: Update Docker Image tag for Ruby to {{ source "RubylatestVersion" }} version
    scmid: default
    spec:
      file: .env
      matchpattern: RUBY_VERSION=.*
      replacepattern: 'RUBY_VERSION={{ source "RubylatestVersion" }}'
  rubyVersionFile:
    kind: file
    sourceid: RubylatestVersion
    name: Update Ruby Version in .ruby-version to {{ source "RubylatestVersion" }}
    scmid: default
    spec:
      file: .ruby-version
      matchpattern: ruby-.*
      replacepattern: 'ruby-{{ source "RubylatestVersion" }}'
  gemfileVersion:
    kind: file
    sourceid: RubylatestVersion
    name: Update Ruby Version in Gemfile to {{ source "RubylatestVersion" }}
    scmid: default
    spec:
      file: Gemfile
      matchpattern: 'ruby "(.*)"'
      replacepattern: 'ruby "{{ source "RubylatestVersion" }}"'

actions:
  default:
    kind: github/pullrequest
    scmid: default
    spec:
      labels:
        - dependencies
    title: Bump Ruby version to {{ source "RubylatestVersion" }}

From top to bottom, this configuration handles:

  1. SCM: Defines the repository where updates will be applied.
  2. Source: Retrieves the latest Ruby version from ruby/ruby GitHub Repository.
  3. Targets: Updates the necessary files ( Gemfile , .ruby-version and .env).
  4. Actions: Creates a pull request proposing these changes.

Whenever we execute updatecli apply we’ll get a pull request as the main outcome:

Pull Request overview

Pull Request overview

Automating Updates with GitHub Actions

Since the goal is fully automating the upgrade process, we integrate GitHub Actions to run updatecli apply on a schedule. Here’s a workflow example that runs on the first day of every month at 01:00:

---
name: Updatecli

on:
  workflow_dispatch:
  schedule:
    # Run At 01:00 on first day-of-month.
    - cron: '0 1 1 * *'

permissions:
  contents: "write"
  pull-requests: "write"

jobs:
  updatecli:
    runs-on: "ubuntu-latest"
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Updatecli in the runner
        uses: updatecli/updatecli-action@v2

      - name: Run Updatecli in apply mode
        run: |
          shopt -s globstar
          updatecli apply --config updatecli/updatecli.d --values updatecli/values.yaml
        env:
          GITHUB_TOKEN: "${{ secrets.UPGRADE_RUBY_TOKEN }}"

Handling the Gemfile.lock Challenge

As mentioned earlier, Updatecli updates three key files:

  • Gemfile
  • .ruby-version
  • .env

Changes proposal from updatecli

Changes proposal from updatecli

However, if you were curious enough to explore the links to the demo repository, you might have noticed that one more file changes in the pull request: Gemfile.lock.

The Gemfile.lock file includes a computed RUBY VERSION field. This value is automatically updated when running bundle install. If left outdated, it can cause issues in CI pipelines or runtime environments due to version mismatches between Gemfile and Gemfile.lock.

Unlike the other files, we can’t just update Gemfile.lock by replacing text—it requires running Bundler to properly regenerate it. That means we need a solution to handle this without manual intervention.

To complete the upgrade, we need to:

  • Checkout the branch created by Updatecli locally.
  • Run bundle update --ruby to properly update Gemfile.lock.
  • Commit and push the changes back to the branch.

Manually doing this every time would defeat the purpose of automation. Instead, let’s see how we can integrate this into our GitHub Actions workflow to handle it automatically.

Automating the Gemfile.lock Update

To overcome this challenge, we have created a second Actions Workflow.

---
name: Bump Ruby Bundled Version

on:
  workflow_dispatch:
  pull_request:
    paths:
      - '.ruby-version'

permissions:
  contents: "write"
  pull-requests: "write"

jobs:
  bump_ruby:
    runs-on: "ubuntu-latest"
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: false
      - name: Update Gemfile.lock
        run: |
          bundle config --local deployment false
          bundle update --ruby
      - name: Commit changes
        run: |
          git config --global user.name "treezio"
          git config --global user.email "bumps@treez.io"
          git add Gemfile.lock
          git commit -m "Update Gemfile.lock with new Ruby version" || echo "No changes to commit"
      - name: Push changes
        uses: ad-m/github-push-action@v0.8.0
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.head_ref }}
          force: true

This workflow will be triggered whenever a pull request containing changes in .ruby-version is created. (In our case, triggering this for Gemfile or .env would result in unnecessary executions).

Once the workflow is triggered, It takes care of:

  1. Configuring bundle
  2. Running bundle update --ruby to update the RUBY VERSION field in Gemfile.lock.
  3. Committing and pushing changes to the remote branch.

Final state of the pull request

Final state of the pull request

Handling GitHub Actions Limitations

GitHub Actions has a **known limitation that prevents one workflow from triggering another if the event comes from an action in a previous workflow. This is a safeguard to avoid infinite recursive executions**.

However, in our case, we need the second workflow (which updates Gemfile.lock) to run when Updatecli creates a pull request. Since PRs created by a GitHub Actions workflow using the built-in secrets.GITHUB_TOKEN do not trigger new workflows, we need a workaround.

If you checked the first workflow, you might have noticed that instead of:

env:
   GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

we use:

env:
   GITHUB_TOKEN: "${{ secrets.UPGRADE_RUBY_TOKEN }}"

This UPGRADE_RUBY_TOKEN is a Personal Access Token (PAT) that grants permissions for:

Reading repository contentsCreating pull requests

Since PAT-generated pull requests do trigger workflows, this approach allows the second workflow (Bump Ruby Bundled Version) to run automatically when .ruby-version changes.

To create the Token go to:

  1. GitHub User Settings
  2. Developer Settings
  3. Personal Access Tokens and choose one of your preference between Fine-Grained (recommended) or Classic Tokens.
  4. Make sure to grant enough permissions over the target repositories where the automation will be executed.
  5. Generate the token and store it safely.

To configure the secret you just created:

  1. navigate to Repository SettingsSecrets and VariablesActions in the repository to automate.
  2. Click New Repository Secret.
  3. Set the name to UPGRADE_RUBY_TOKEN.
  4. Paste the token value and save.

Final Outcome

With this setup, every time Updatecli proposes a Ruby version update via pull request:

.ruby-version, Gemfile, and .env are updated automatically. ✅ Gemfile.lock is updated via GitHub Actions using bundle. ✅ A fully automated pull request is ready for review & merge.

This ensures that upgrading Ruby is a hands-off process for developers. 🚀


메타데이터
post_id
f71d19e26aeb
slug
automating-ruby-version-upgrades-f71d19e26aeb
url
https://medium.com/sequra-tech/automating-ruby-version-upgrades-f71d19e26aeb
canonical_url
https://medium.com/sequra-tech/automating-ruby-version-upgrades-f71d19e26aeb
author_url
https://medium.com/@treezio
status
ok
fetched_at
2026-06-11 12:34:08