<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mohamed Magdy Omar</title>
    <description>The latest articles on DEV Community by Mohamed Magdy Omar (@its-magdy).</description>
    <link>https://dev.to/its-magdy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3850131%2F8a9eda23-5d9a-4bae-9591-7b91db43e0c8.jpeg</url>
      <title>DEV Community: Mohamed Magdy Omar</title>
      <link>https://dev.to/its-magdy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/its-magdy"/>
    <language>en</language>
    <item>
      <title>rails-local-ci: Essential Local CI for Older Rails Apps</title>
      <dc:creator>Mohamed Magdy Omar</dc:creator>
      <pubDate>Mon, 30 Mar 2026 09:13:05 +0000</pubDate>
      <link>https://dev.to/its-magdy/essential-rails-local-ci-for-older-apps-rails-local-ci-bb-signoff-4m5l</link>
      <guid>https://dev.to/its-magdy/essential-rails-local-ci-for-older-apps-rails-local-ci-bb-signoff-4m5l</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/rails/rails/pull/54693" rel="noopener noreferrer"&gt;Rails 8.1&lt;/a&gt; shipped a genuinely useful feature: a standardized &lt;code&gt;bin/ci&lt;/code&gt; script backed by &lt;code&gt;ActiveSupport::ContinuousIntegration&lt;/code&gt;. Run your full test suite, linter, and security checks locally before pushing. No cloud bill. No waiting in a CI queue. Just your laptop doing what it was built for.&lt;/p&gt;

&lt;p&gt;The catch? I'm on Rails 7. And I use Bitbucket, not GitHub.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;bin/ci&lt;/code&gt; wasn't available to me yet. And Basecamp's &lt;code&gt;gh-signoff&lt;/code&gt; — the companion tool that posts a green commit status to your host after local CI passes — only works with GitHub. Bitbucket users don't get a version of that either.&lt;/p&gt;

&lt;p&gt;I built both myself. This post explains what each tool does, how they connect, and why the upgrade path for &lt;code&gt;rails-local-ci&lt;/code&gt; is the whole point of the project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;code&gt;rails-local-ci&lt;/code&gt; brings Rails 8.1's &lt;code&gt;bin/ci&lt;/code&gt; workflow to Rails 5.2–7.x. &lt;code&gt;bb-signoff&lt;/code&gt; posts green commit statuses to Bitbucket after local tests pass. Use them together for a full local CI -&amp;gt; signoff -&amp;gt; merge workflow. Remove the gem when you hit Rails 8.1 — nothing else changes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is Rails Local CI — And Why Doesn't It Work on Rails 7?
&lt;/h2&gt;

&lt;p&gt;Rails 8.1's local CI pattern is elegant. You define your steps in &lt;code&gt;config/ci.rb&lt;/code&gt;, run &lt;code&gt;./bin/ci&lt;/code&gt;, and you're done. It sets &lt;code&gt;ENV["CI"]=true&lt;/code&gt;, handles colorized output, supports parallel steps, and stops early on failure if you want. It's exactly what a lot of teams have been cobbling together with shell scripts for years.&lt;/p&gt;

&lt;p&gt;But if your app runs Rails 5.2 through 7.x, that class doesn't exist. And even if it did, posting a green commit status back to Bitbucket Cloud still requires hitting the REST API directly. There's no official tooling for it.&lt;/p&gt;

&lt;p&gt;These two problems led to two projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does rails-local-ci Backport the bin/ci Workflow?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/its-magdy/rails-local-ci" rel="noopener noreferrer"&gt;rails-local-ci&lt;/a&gt; is a Ruby gem that backports &lt;code&gt;ActiveSupport::ContinuousIntegration&lt;/code&gt; to Rails 5.2 through 7.x. Not a reimplementation with its own API. The actual class, placed at the identical load path Rails 8.1 uses.&lt;/p&gt;

&lt;p&gt;That last part matters. Here's why.&lt;/p&gt;

&lt;p&gt;When you're ready to upgrade to Rails 8.1, you remove the gem from your Gemfile and run &lt;code&gt;bundle install&lt;/code&gt;. That's it. You don't touch &lt;code&gt;bin/ci&lt;/code&gt;. You don't touch &lt;code&gt;config/ci.rb&lt;/code&gt;. Rails 8.1 ships the class at &lt;code&gt;active_support/continuous_integration&lt;/code&gt;, which is the same path the gem uses. Your files just keep working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing rails-local-ci
&lt;/h3&gt;

&lt;p&gt;Add it to your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"rails-local-ci"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rails generate rails_local_ci:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator creates two files: &lt;code&gt;bin/ci&lt;/code&gt; (the runner) and &lt;code&gt;config/ci.rb&lt;/code&gt; (your step definitions).&lt;/p&gt;

&lt;h3&gt;
  
  
  What config/ci.rb looks like
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Run using bin/ci&lt;/span&gt;

&lt;span class="no"&gt;CI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Setup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bin/setup --skip-server"&lt;/span&gt;

  &lt;span class="c1"&gt;# Run independent checks in parallel for faster feedback:&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;#   group "Checks", parallel: 3 do&lt;/span&gt;
  &lt;span class="c1"&gt;#     step "Style: Ruby",        "bin/rubocop"          if File.exist?("bin/rubocop")&lt;/span&gt;
  &lt;span class="c1"&gt;#     step "Security: Brakeman", "bin/brakeman --quiet"  if File.exist?("bin/brakeman")&lt;/span&gt;
  &lt;span class="c1"&gt;#     step "Security: Gems",     "bin/bundler-audit"     if File.exist?("bin/bundler-audit")&lt;/span&gt;
  &lt;span class="c1"&gt;#   end&lt;/span&gt;

  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Style: Ruby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="s2"&gt;"bin/rubocop"&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/rubocop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Security: Gem audit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="s2"&gt;"bin/bundler-audit"&lt;/span&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/bundler-audit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Security: Brakeman code analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/brakeman"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Tests: Rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="s2"&gt;"bin/rails test"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output is colorized, each step shows pass/fail and elapsed time, and the whole run sets &lt;code&gt;ENV["CI"]="true"&lt;/code&gt; so Rails behaves the same way it does in your cloud CI pipeline.&lt;/p&gt;

&lt;p&gt;You can also pass &lt;code&gt;--fail-fast&lt;/code&gt; (or &lt;code&gt;-f&lt;/code&gt;) to stop at the first failing step instead of running everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  bb-signoff: How Do Bitbucket Users Get a Signoff Tool?
&lt;/h2&gt;

&lt;p&gt;Running tests locally is only half the story. The other half is communicating to your team: "I ran this. It passed. The PR is ready."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/basecamp/gh-signoff" rel="noopener noreferrer"&gt;Basecamp's &lt;code&gt;gh-signoff&lt;/code&gt;&lt;/a&gt; does exactly this for GitHub — it posts a commit status with a green check that you can make required before merging. But it calls the GitHub API. Bitbucket has an equivalent &lt;a href="https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commit-statuses/" rel="noopener noreferrer"&gt;Bitbucket REST API&lt;/a&gt;. Nobody had built the tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/its-magdy/bb-signoff" rel="noopener noreferrer"&gt;bb-signoff&lt;/a&gt; is that tool. It's a single Bash script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing bb-signoff
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/its-magdy/bb-signoff/v1.0.0/bb-signoff &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/bb-signoff &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/bb-signoff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a Bitbucket repository access token (Repository: Read/Write/Admin, Pull Requests: Read/Write), then store it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.bb-signoff &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
BB_API_TOKEN=ATCTT3...
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.bb-signoff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using it
&lt;/h3&gt;

&lt;p&gt;After your tests pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bb-signoff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That posts a green commit status to Bitbucket for the current HEAD. You can also use named contexts to break signoff into separate checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bb-signoff tests lint security
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which shows up in Bitbucket as three distinct statuses. If you want Bitbucket to warn you before merging when signoff hasn't passed, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bb-signoff &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That adds a Bitbucket branch restriction requiring passing builds before merge. Remove it with &lt;code&gt;bb-signoff uninstall&lt;/code&gt;. Check whether it's active with &lt;code&gt;bb-signoff check&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One guard worth knowing: &lt;code&gt;bb-signoff&lt;/code&gt; will refuse to post a status if you have uncommitted or unpushed changes. You can override with &lt;code&gt;-f&lt;/code&gt;, but the default is intentional. The point is to certify a specific commit, not a working tree.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Does the Full Local CI Workflow Look Like Together?
&lt;/h2&gt;

&lt;p&gt;Here's what the combined setup looks like in &lt;code&gt;config/ci.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;CI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Setup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bin/setup --skip-server"&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Style: Ruby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="s2"&gt;"bin/rubocop"&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/rubocop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Security: Gem audit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="s2"&gt;"bin/bundler-audit"&lt;/span&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/bundler-audit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Security: Brakeman code analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bin/brakeman"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Tests: Rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="s2"&gt;"bin/rails test"&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;success?&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="s2"&gt;"Signoff: All systems go. Ready for merge and deploy."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bb-signoff"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="s2"&gt;"CI failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Fix the issues above before submitting your PR"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;./bin/ci&lt;/code&gt;. If everything passes, &lt;code&gt;bb-signoff&lt;/code&gt; fires automatically. Your PR gets a green status. If you've run &lt;code&gt;bb-signoff install&lt;/code&gt; on the branch, Bitbucket will warn you before merging if the signoff check hasn't passed — so it won't go unnoticed.&lt;/p&gt;

&lt;p&gt;When all steps pass, the terminal prints each step in green with its elapsed time, then exits with code 0 — at which point &lt;code&gt;bb-signoff&lt;/code&gt; runs and posts to Bitbucket. If any step fails, the run exits early and &lt;code&gt;bb-signoff&lt;/code&gt; never fires, so your PR won't show a false green. After a successful signoff, the Bitbucket PR view shows a green build status indicator next to the commit SHA, the same way a cloud CI run would appear.&lt;/p&gt;

&lt;p&gt;No cloud CI required. Your laptop is the CI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Is the Upgrade Story the Whole Point of rails-local-ci?
&lt;/h2&gt;

&lt;p&gt;I want to be direct about the design decision in &lt;code&gt;rails-local-ci&lt;/code&gt;, because it's not obvious from the outside.&lt;/p&gt;

&lt;p&gt;The gem places &lt;code&gt;ActiveSupport::ContinuousIntegration&lt;/code&gt; at &lt;code&gt;lib/active_support/continuous_integration.rb&lt;/code&gt;. Rails 8.1 ships the class at the same path. When &lt;code&gt;require "active_support/continuous_integration"&lt;/code&gt; runs in &lt;code&gt;bin/ci&lt;/code&gt;, Ruby's load path resolves it. On Rails 7, the gem wins. On Rails 8.1, the gem is gone and Rails itself wins.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;bin/ci&lt;/code&gt; and &lt;code&gt;config/ci.rb&lt;/code&gt; never change. The upgrade step is one line in your Gemfile.&lt;/p&gt;

&lt;p&gt;That's the whole bet: write your CI config once, against a stable API, and don't rewrite it when you upgrade Rails. The gem is a bridge, not a permanent dependency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It, Star It, Open Issues
&lt;/h2&gt;

&lt;p&gt;Both projects are open source and MIT licensed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rails-local-ci&lt;/strong&gt;: &lt;a href="https://github.com/its-magdy/rails-local-ci" rel="noopener noreferrer"&gt;github.com/its-magdy/rails-local-ci&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bb-signoff&lt;/strong&gt;: &lt;a href="https://github.com/its-magdy/bb-signoff" rel="noopener noreferrer"&gt;github.com/its-magdy/bb-signoff&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're on older Rails and want the &lt;code&gt;bin/ci&lt;/code&gt; workflow now, give &lt;code&gt;rails-local-ci&lt;/code&gt; a try. If you're on Bitbucket and have been wishing &lt;code&gt;gh-signoff&lt;/code&gt; existed for your platform, &lt;code&gt;bb-signoff&lt;/code&gt; is ready.&lt;/p&gt;

&lt;p&gt;Bug reports and PRs are welcome on both. If something doesn't work for your setup, open an issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://github.com/its-magdy" rel="noopener noreferrer"&gt;Magdy&lt;/a&gt; — a Software Engineer who got tired of waiting for Rails 8.1 and didn't have a signoff tool for Bitbucket. So he built both.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>opensource</category>
      <category>ci</category>
      <category>bitbucket</category>
    </item>
  </channel>
</rss>
