Automating Ruby Version Upgrades
Simplify Ruby version upgrades — here’s your guide!
Automating Ruby Version Upgrades
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-versionwhich 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:
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:
- Fetch the latest Ruby version: It retrieves the newest available Ruby release (or any other version based on filters).
- Update repository files — It applies the new Ruby version to relevant files.
- 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:
- SCM: Defines the repository where updates will be applied.
- Source: Retrieves the latest Ruby version from
ruby/rubyGitHub Repository. - Targets: Updates the necessary files (
Gemfile,.ruby-versionand.env). - 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
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
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 --rubyto properly updateGemfile.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:
- Configuring
bundle - Running
bundle update --rubyto update theRUBY VERSIONfield inGemfile.lock. - Committing and pushing changes to the remote branch.

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 contents ✅ Creating 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:
- GitHub User Settings
- Developer Settings
- Personal Access Tokens and choose one of your preference between Fine-Grained (recommended) or Classic Tokens.
- Make sure to grant enough permissions over the target repositories where the automation will be executed.
- Generate the token and store it safely.
To configure the secret you just created:
- navigate to Repository Settings → Secrets and Variables → Actions in the repository to automate.
- Click New Repository Secret.
- Set the name to
UPGRADE_RUBY_TOKEN. - 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