Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bazel for Ruby (RubyKaigi 2025)

Bazel for Ruby (RubyKaigi 2025)

A talk about using Bazel in Ruby projects for RubyKaigi 2025 (https://rubykaigi.org/2025/presentations/p0deje.html).

Alex Rodionov

April 17, 2025
Tweet

More Decks by Alex Rodionov

Other Decks in Technology

Transcript

  1. What is Bazel? Build and test tool for monorepos Created

    at Google and open sourced Polyglot (Java, Python, Go, JavaScript, etc.) E ffi cient and scalable Hermetic and reproducible Distributed (remote caching and execution)
  2. Canonical Bazel ruleset supporting Ruby language Maintained by Bazel Rules

    Authors SIG Available in Bazel Central Registry Used by companies (Stripe, etc.) and open-source projects (Protobuf, Selenium, etc.) rules_ruby
  3. Installs Ruby interpreter (CRuby, JRuby, Tru ff l eRuby) Installs

    dependencies via Bundler Provides rules for common operations: rb_library rb_binary rb_test rb_gem_build rb_gem_push rules_ruby
  4. . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock │ │ ├─

    lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  5. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  6. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  7. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  8. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  9. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  10. bazel_dep(name = "rules_ruby", version = "0.18.0") ruby = use_extension( "@rules_ruby

    // ruby:extensions.bzl", "ruby" ) ruby.toolchain( name = "ruby", version = "3.4.2", ) ruby.bundle_fetch( name = "bundle", gemfile = " //: Gemfile", gemfile_lock = " //: Gemfile.lock", ) use_repo(ruby, "bundle", "ruby", "ruby_toolchains") register_toolchains("@ruby_toolchains // :all") . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel │ ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  11. load( "@rules_ruby // ruby:defs.bzl", "rb_gem_build", ) rb_gem_build( name = "calendar",

    gemspec = "calendar.gemspec", deps = [" / / lib"], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  12. load( "@rules_ruby // ruby:defs.bzl", "rb_gem_build", ) rb_gem_build( name = "calendar",

    gemspec = "calendar.gemspec", deps = [" / / lib"], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  13. load( "@rules_ruby // ruby:defs.bzl", "rb_gem_build", ) rb_gem_build( name = "calendar",

    gemspec = "calendar.gemspec", deps = [" / / lib"], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  14. $ bazel build // :calendar … ERROR : no such

    package 'lib': BUILD file not found in any of the following directories. Add a BUILD file to a directory to mark it as a package. … . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ │ │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  15. load("@rules_ruby // ruby:defs.bzl", "rb_library") rb_library( name = "lib", srcs =

    ["calendar.rb"], visibility = [" // visibility:public"], deps = [ " // lib/calendar:add", " // lib/calendar:remove", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  16. load("@rules_ruby // ruby:defs.bzl", "rb_library") rb_library( name = "lib", srcs =

    ["calendar.rb"], visibility = [" // visibility:public"], deps = [ " // lib/calendar:add", " // lib/calendar:remove", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  17. load("@rules_ruby // ruby:defs.bzl", "rb_library") rb_library( name = "lib", srcs =

    ["calendar.rb"], visibility = [" // visibility:public"], deps = [ " // lib/calendar:add", " // lib/calendar:remove", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ │ │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  18. load("@rules_ruby // ruby:defs.bzl", "rb_library") rb_library( name = "add", srcs =

    ["add.rb"], visibility = [" // visibility:public"], ) rb_library( name = "remove", srcs = ["remove.rb"], visibility = [" // visibility:public"], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  19. $ bazel build // :calendar … INFO : From GemBuild

    calendar.gem: Successfully built RubyGem Name: calendar Version: 0.1.0 File: calendar-0.1.0.gem … bazel - bin/calendar.gem … . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  20. load( "@rules_ruby // ruby:defs.bzl", "rb_gem_build", "rb_gem_push", ) rb_gem_build( name =

    "calendar", gemspec = "calendar.gemspec", deps = [" / / lib"], ) rb_gem_push( name = "calendar - release", gem = ":calendar", ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  21. $ bazel run // :calendar - release … Successfully built

    RubyGem Name: calendar Version: 0.1.0 File: calendar-0.1.0.gem … Pushing gem to https: // rubygems.org… Successfully registered gem: calendar (0.1.0) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec │ ├─ add_spec.rb └─ remove_spec.rb
  22. load("@rules_ruby // ruby:defs.bzl", "rb_test") rb_test( name = "add", srcs =

    ["add_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:add", "@bundle", ], ) rb_test( name = "remove", srcs = ["remove_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:remove", "@bundle", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  23. load("@rules_ruby // ruby:defs.bzl", "rb_test") rb_test( name = "add", srcs =

    ["add_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:add", "@bundle", ], ) rb_test( name = "remove", srcs = ["remove_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:remove", "@bundle", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  24. load("@rules_ruby // ruby:defs.bzl", "rb_test") rb_test( name = "add", srcs =

    ["add_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:add", "@bundle", ], ) rb_test( name = "remove", srcs = ["remove_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:remove", "@bundle", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  25. load("@rules_ruby // ruby:defs.bzl", "rb_test") rb_test( name = "add", srcs =

    ["add_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:add", "@bundle", ], ) rb_test( name = "remove", srcs = ["remove_spec.rb"], main = "@bundle // bin:rspec", deps = [ " // lib/gem:remove", "@bundle", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  26. $ bazel test //. .. … // spec:add PASSED in

    1.8s // spec:remove PASSED in 1.2s … . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  27. load( "@rules_ruby // ruby:defs.bzl", "rb_gem_build", "rb_gem_push", "rb_test", ) … rb_test(

    name = "rubocop", srcs = ["Gemfile"], main = "@bundle // bin:rubocop", deps = [ " // lib", " // spec:add", " // spec:remove", "@bundle", ], ) . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  28. $ bazel test //. .. … // spec:add (cached) PASSED

    in 1.8s // spec:remove (cached) PASSED in 1.2s // :rubocop PASSED in 3.5s … . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  29. $ bazel test //. .. … // spec:add (cached) PASSED

    in 1.8s // spec:remove (cached) PASSED in 1.2s // :rubocop PASSED in 3.5s … . ├─ calendar.gemspec ├─ Gemfile ├─ Gemfile.lock ├─ MODULE.bazel ├─ BUILD ├─ lib │ ├─ BUILD │ ├─ calendar │ │ ├─ BUILD │ │ ├─ add.rb │ │ └─ remove.rb │ └─ calendar.rb └─ spec ├─ BUILD ├─ add_spec.rb └─ remove_spec.rb
  30. Parallel test execution (parallel_tests) Test regression selection (crystalball) Packages and

    visibility control (packwerk) Remote build and test caching Remote build and test execution (only JRuby) Please, CRuby team, make standalone prebuilt Ruby that doesn't require compiling! Compile C/Java programs with CRuby/JRuby Done in rules_ruby
  31. Test sharding (knapsack) Test coverage (simplecov) Aspects (e.g. automatically typecheck

    during build) Better Gem fi le support (self-hosted RubyGems, gems from Git repositories) Better debugging support (rdbg) Todo in rules_ruby