|
| 1 | +--- |
| 2 | +title: Upgrading to RubyGems/Bundler 4 |
| 3 | +layout: post |
| 4 | +author: Hiroshi SHIBATA |
| 5 | +author_email: hsbt@ruby-lang.org |
| 6 | +--- |
| 7 | + |
| 8 | +We introduced breaking changes in RubyGems/Bundler 4 in order to improve usability, security, and maintainability of the tool. This document describes the changes that you will find when upgrading to RubyGems 4 and Bundler 4, and how to prepare for them while still using Bundler 2.7. |
| 9 | + |
| 10 | +## RubyGems 4 |
| 11 | + |
| 12 | +### CLI behavior changes |
| 13 | + |
| 14 | +#### Removed `gem query` command |
| 15 | + |
| 16 | +Please use `gem search` or `gem list` instead. |
| 17 | + |
| 18 | +#### Completely removed `gem install --default` feature |
| 19 | + |
| 20 | +The `--default` option was confusing for RubyGems users and caused broken installs. |
| 21 | + |
| 22 | +This was an unfinished feature originally intended to install gems directly into the Ruby standard library location, but it only generated executables without properly copying library files. This partial implementation led to a complicated environment with no real benefit for users. |
| 23 | + |
| 24 | +### API behavior changes |
| 25 | + |
| 26 | +#### No replacements for removed deprecated methods |
| 27 | + |
| 28 | +The following deprecated methods have been removed with no replacement: |
| 29 | + |
| 30 | +* `Gem::Specification#has_rdoc`, `has_rdoc=` and `has_rdoc?` |
| 31 | +* `Gem::DependencyInstaller#find_gems_with_sources` has been removed |
| 32 | +* `Gem::Util.silent_system` |
| 33 | +* `Gem::Specification#validate_metadata`, `validate_dependencies` and `validate_permissions` |
| 34 | +* `Gem::Specification#default_executable` |
| 35 | +* `Gem::Installer#unpack` |
| 36 | + |
| 37 | +#### Removed deprecated Gem::Platform.match |
| 38 | + |
| 39 | +Please use `Gem::Platform.match_spec?` or `match_gem?` instead. |
| 40 | + |
| 41 | +#### Removed deprecated `Gem::BasicSpecification.default_specifications_dir` |
| 42 | + |
| 43 | +Please use `Gem.default_specifications_dir` instead. |
| 44 | + |
| 45 | +## Bundler 4 |
| 46 | + |
| 47 | +### Bundler 4 simulation mode |
| 48 | + |
| 49 | +In order to prepare for Bundler 4, you can easily configure Bundler 2.7 to behave exactly like Bundler 4 will behave. To do so, you have three options: |
| 50 | + |
| 51 | +* Set the environment variable `BUNDLE_SIMULATE_VERSION` to `4`. |
| 52 | +* Run `bundle config set --global simulate_version 4`. |
| 53 | +* Run `bundle config set --local simulate_version 4`. |
| 54 | + |
| 55 | +From now on in this document we will assume that all three of these configuration options are available, but will only mention `bundle config set <option> <value>`. |
| 56 | + |
| 57 | +The following is a summary of the changes that we introduced in Bundler 4, and why we made those changes. Some of them should be well known already by existing users, because we have been printing deprecation messages for years, but some of them are defaults that were switched in Bundler 4. |
| 58 | + |
| 59 | +### CLI behavior changes |
| 60 | + |
| 61 | +#### Running just `bundle` to mean `bundle install` is not recommended anymore |
| 62 | + |
| 63 | +We changed this default to make Bundler more friendly for new users. We do understand that long time users already know how Bundler works and found it useful that just `bundle` defaulted to `bundle install`. |
| 64 | + |
| 65 | +Currently, Bundler uses `install_or_cli_help` by default for backward compatibility. This automatically uses `bundle install` or shows help depending on the context. |
| 66 | + |
| 67 | +If you want to keep the current behavior (defaulting to `bundle install`), you can explicitly configure: |
| 68 | + |
| 69 | +```sh |
| 70 | +bundle config set default_cli_command install_or_cli_help --global |
| 71 | +``` |
| 72 | + |
| 73 | +However, if you want to adopt the new behavior immediately where `bundle` only shows help, you can configure: |
| 74 | + |
| 75 | +```sh |
| 76 | +bundle config set default_cli_command cli_help --global |
| 77 | +``` |
| 78 | + |
| 79 | +Please use `bundle install` explicitly in your scripts and documentation, so that everyone is clear about what is happening. |
| 80 | + |
| 81 | +#### Flags passed to `bundle install` that relied on being remembered across invocations have been removed |
| 82 | + |
| 83 | +In particular, the `--clean`, `--deployment`, `--frozen`, `--no-prune`, `--path`, `--shebang`, `--system`, `--without`, and `--with` options to `bundle install`. |
| 84 | + |
| 85 | +Remembering CLI options has been a source of historical confusion and bug reports, not only for beginners but also for experienced users. |
| 86 | + |
| 87 | +A CLI tool should not behave differently across exactly the same invocations _unless_ explicitly configured to do so. This is what configuration is about after all, and things should never be silently configured without the user knowing about it. |
| 88 | + |
| 89 | +The problem with this behavior was that very common workflows were relying on it. For example, when you ran `bundle install --without development:test` in production, those flags were persisted in the app's configuration file and further `bundle` invocations would happily ignore development and test gems. |
| 90 | + |
| 91 | +This magic has been removed from Bundler 4, and you now explicitly need to configure it, either through environment variables, application configuration, or machine configuration. For example, with `bundle config set --local without development test`. |
| 92 | + |
| 93 | +#### `bundle viz` has been removed and extracted to a plugin. |
| 94 | + |
| 95 | +This was the only bundler command requiring external dependencies, both an OS dependency (the `graphviz` package) and a gem dependency (the `ruby-graphviz` gem). Removing these dependencies made development easier and it was also seen by the bundler team as an opportunity to develop a bundler plugin that is officially maintained by the RubyGems team, and that users can take as a reference to develop their own plugins. |
| 96 | + |
| 97 | +The new plugin is called `bundler-graph` and it is available at https://github.com/rubygems/bundler-grap now. |
| 98 | + |
| 99 | +The plugin contains the same code as the old core command, the only difference being that the command is now implemented as `bundle graph` which is much easier to understand. |
| 100 | + |
| 101 | +#### The `bundle install` command no longer accepts a `--binstubs` flag. |
| 102 | + |
| 103 | +The `--binstubs` option has been removed from `bundle install` and replaced with the `bundle binstubs` command. |
| 104 | + |
| 105 | +The `--binstubs` flag would create binstubs for all executables present inside the gems in the project. This was hardly useful since most users only use a subset of all the binstubs available to them. Also, it would force the introduction of a bunch of most likely unused files into source control. Because of this, binstubs now must be created and checked into version control individually. |
| 106 | + |
| 107 | +If you still want to create binstubs for all gems, you can run: |
| 108 | + |
| 109 | +```sh |
| 110 | +bundle binstubs --all |
| 111 | +``` |
| 112 | + |
| 113 | +#### The `bundle inject` command has been replaced with `bundle add` |
| 114 | + |
| 115 | +We believe the new command fits the user's mental model better and it supports a wider set of use cases. |
| 116 | + |
| 117 | +The interface supported by `bundle inject` works exactly the same in `bundle add`, so it should be easy to migrate to the new command. |
| 118 | + |
| 119 | +### Gemfile and lockfile behavior changes |
| 120 | + |
| 121 | +#### Bundler includes checksums in new lockfiles by default |
| 122 | + |
| 123 | +We shipped this security feature and turned it on by default, so that everyone benefits from the extra security assurances. So whenever you create a new lockfile, Bundler now includes a CHECKSUMS section. |
| 124 | + |
| 125 | +Bundler will not automatically add a CHECKSUMS section to existing lockfiles, though, unless explicitly requested through `bundle lock --add-checksums`. |
| 126 | + |
| 127 | +#### Strict source pinning in Gemfile is enforced by default |
| 128 | + |
| 129 | +In Bundler 4, the source for every dependency is now unambiguously defined, and Bundler refuses to run otherwise. |
| 130 | + |
| 131 | +##### Multiple global Gemfile sources are no longer supported. |
| 132 | +Instead of something like this: |
| 133 | + |
| 134 | +```ruby |
| 135 | +source "https://main_source" |
| 136 | +source "https://another_source" |
| 137 | + |
| 138 | +gem "dependency1" |
| 139 | +gem "dependency2" |
| 140 | +``` |
| 141 | + |
| 142 | +do something like this: |
| 143 | + |
| 144 | +```ruby |
| 145 | +source "https://main_source" |
| 146 | + |
| 147 | +gem "dependency1" |
| 148 | + |
| 149 | +source "https://another_source" do |
| 150 | + gem "dependency2" |
| 151 | +end |
| 152 | +``` |
| 153 | + |
| 154 | +##### Global `path` and `git` sources are no longer supported. |
| 155 | + |
| 156 | +Instead of something like this: |
| 157 | + |
| 158 | +```ruby |
| 159 | +path "/my/path/with/gems" |
| 160 | +git "https://my_git_repo_with_gems" |
| 161 | + |
| 162 | +gem "dependency1" |
| 163 | +gem "dependency2" |
| 164 | +``` |
| 165 | + |
| 166 | +do something like this: |
| 167 | + |
| 168 | +```ruby |
| 169 | +gem "dependency1", path: "/my/path/with/gems" |
| 170 | +gem "dependency2", git: "https://my_git_repo_with_gems" |
| 171 | +``` |
| 172 | + |
| 173 | +or use the block forms if you have multiple gems for each source and you want to be a bit DRYer: |
| 174 | + |
| 175 | +```ruby |
| 176 | +path "/my/path/with/gems" do |
| 177 | + # gem "dependency1" |
| 178 | + # ... |
| 179 | + # gem "dependencyn" |
| 180 | +end |
| 181 | + |
| 182 | +git "https://my_git_repo_with_gems" do |
| 183 | + # gem "dependency1" |
| 184 | + # ... |
| 185 | + # gem "dependencyn" |
| 186 | +end |
| 187 | +``` |
| 188 | + |
| 189 | +### Cache behavior changes |
| 190 | + |
| 191 | +#### Git and Path gems are included in `vendor/cache` by default |
| 192 | + |
| 193 | +If you have a `vendor/cache` directory (to support offline scenarios, for example), Bundler now includes gems from `path` and `git` sources in there. |
| 194 | + |
| 195 | +We're unsure why these gems were treated specially so we'll start caching them normally. |
| 196 | + |
| 197 | +#### Bundler uses cached local data if available when network issues are found during resolution |
| 198 | + |
| 199 | +Just trying to provide a more resilient behavior here. |
| 200 | + |
| 201 | +### API behavior changes |
| 202 | + |
| 203 | +#### `Bundler.clean_env`, `Bundler.with_clean_env`, `Bundler.clean_system`, and `Bundler.clean_exec` have been removed |
| 204 | + |
| 205 | +All of these helpers ultimately used `Bundler.clean_env` under the hood, which made sure all bundler-related environment variables were removed inside the block it yields. |
| 206 | + |
| 207 | +After quite a lot of user reports, we noticed that users don't usually want this but instead want the bundler environment as it was before the current process was started. Thus, `Bundler.with_original_env`, `Bundler.original_system`, and `Bundler.original_exec` were born. They all use the new `Bundler.original_env` under the hood. |
| 208 | + |
| 209 | +There are however some specific cases where the good old `Bundler.clean_env` behavior can be useful. For example, when testing Rails generators, you really want an environment where `bundler` is out of the picture. This is why we decided to keep the old behavior under a new more clear name, because we figured the word "clean" was too ambiguous. So we introduced `Bundler.unbundled_env`, `Bundler.with_unbundled_env`, `Bundler.unbundled_system`, and `Bundler.unbundled_exec`. |
| 210 | + |
| 211 | +#### `Bundler.environment` has been deprecated in favor of `Bundler.load`. |
| 212 | + |
| 213 | +We're not sure how people might be using this directly but we removed the `Bundler::Environment` class which was instantiated by `Bundler.environment` since we realized the `Bundler::Runtime` class was the same thing. `Bundler.environment` now delegates to `Bundler.load`, which holds the reference to the `Bundler::Runtime`. |
| 214 | + |
| 215 | +#### Removed public methods of `Bundler::SpecSet` |
| 216 | + |
| 217 | +The following public methods of `Bundler::SpecSet` have been removed with no replacement: |
| 218 | + |
| 219 | +* `SpecSet#-` |
| 220 | +* `SpecSet#<<` |
| 221 | + |
| 222 | +#### `SpecSet#for` always implicitly performs validation |
| 223 | + |
| 224 | +`SpecSet#for` received a `check` parameter, but that's no longer used and deprecated. Please remove this parameter. |
| 225 | + |
| 226 | +#### `CurrentRuby#maglev?` was removed with no replacement. |
| 227 | + |
| 228 | +Please use the built-in Ruby `RUBY_ENGINE` constant to check the Ruby implementation you are running on. |
| 229 | + |
| 230 | +#### `Bundler.rubygems.all_specs` has been removed |
| 231 | + |
| 232 | +Please use `Bundler.rubygems.installed_specs` instead. |
| 233 | + |
| 234 | +### Other notable changes |
| 235 | + |
| 236 | +#### Deployment helpers for `vlad` and `capistrano` have been removed. |
| 237 | + |
| 238 | +These were natural deprecations since the `vlad` tool has had no activity for years whereas `capistrano` 3 has built-in Bundler integration in the form of the `capistrano-bundler` gem, and everyone using Capistrano 3 should already be using that instead. If for some reason, you are still using Capistrano 2, feel free to copy the Capistrano tasks out of the Bundler 2 file `lib/bundler/deployment.rb` and put them into your app. |
| 239 | + |
| 240 | +In general, we don't want to maintain integrations for every deployment system out there, so that's why we removed these. |
0 commit comments