BUNDLE_APP_CONFIG which is troublesome when developing Ruby with DevContainer

TL;DR

--The environment variable BUNDLE_APP_CONFIG set in the official Docker ruby image can get in the way of development within DevContainer. --If you want to eliminate the influence of BUNDLE_APP_CONFIG, write the following in devcontainer.json.

json:.devcontainer/devcontainer.json


{
  // ...
  "remoteEnv": {
    "BUNDLE_APP_CONFIG": null
  },
  // ...
}

Troublesome BUNDLE_APP_CONFIG

VSCode Remote --In most cases of Ruby development with Containers, you will create a DevContainer based on Docker official ruby image [^ 1] I suppose, but this image has a nasty problem.

That is the environment variable BUNDLE_APP_CONFIG. It is set to / usr / local / bundle by default by the ʻENV` instruction in the Dockerfile.

BUNDLE_APP_CONFIG is an environment variable that specifies where to save bundler's application-local settings (the settings created when you do bundle config set --local). If this environment variable is not set, bundler will create a setting under .bundle / in the application root.

Now, let's move on to the case of Ruby development with DevContainer of VSCode, in order to mount the installed gem on the container while making it persistent, I would like to plunge it into the vendor directory etc. in the workspace as follows. [^ 2]

$ bundle config set --local path vendor/bundle

This setting should usually save the configuration file with the path setting as .bundle / config. By setting it locally, a setting file is created in the workspace, and even if the container is destroyed, there is no need to reconfigure it. However, as mentioned above, BUNDLE_APP_CONFIG is already set in the official Docker ruby image, so this local setting is saved in / usr / local / bundle / config.

If you check with bundle config, it looks like the following.

$ bundle config
Settings are listed in order of priority. The top value will be used.
path
Set for your local app (/usr/local/bundle/config): "vendor/bundle"

app_config
Set via BUNDLE_APP_CONFIG: "/usr/local/bundle"

silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true

In this case, the settings will be lost when the container is destroyed by Rebuild etc., so execute bundle config ... each time, or mount / usr / local / bundle from the host. You will need to devise.

The smart solution is to do something about BUNDLE_APP_CONFIG itself.

By the way, BUNDLE_APP_CONFIG is set in the official image in the opposite way of thinking, it seems to be" to prevent .bundle / config on the host side from affecting the inside of the container" [^ 3 ].

__2020 / 09/30 Addendum __ If you only work inside the container (= you don't need to share .bundle / or vendor / with the host), leave BUNDLE_APP_CONFIG and leave the separately created volume / usr / local / bundle It seems better to mount it on. Especially in Docker for Mac, Disk I / O at the time of bind mount is slow, so if you set gem in the workspace, it may take time such as bundle install.

Countermeasures

× Set an empty string in BUNDLE_APP_CONFIG

This is the method described in the Official Documentation.

The environment variables we set are canonically listed in the above-linked Dockerfiles, but some of them include GEM_HOME, BUNDLE_PATH, BUNDLE_BIN, BUNDLE_SILENCE_ROOT_WARNING, and BUNDLE_APP_CONFIG.

If these cause issues for your use case (running multiple Ruby applications in a single container, for example), setting them to the empty string should be sufficient for undoing their behavior.

The bottom line is that this method doesn't work. Since bundler only looks at the existence of BUNDLE_APP_CONFIG [^ 4], if an empty string is set, a config file will be created directly under the current directory.

Let's experiment. Set the BUNDLE_APP_CONFIG to an empty string in the Dockerfile for DevCotainer.

devcontainer/Dockerfile


FROM ruby:2.7

#Set an empty string
ENV BUNDLE_APP_CONFIG=

#The following is omitted

Build DevContaier and go inside. For some reason BUNDLE_APP_CONFIG disappears on the terminal and it works fine (for unknown reasons it may be a VSCode implementation issue).

$ bundle config
Settings are listed in order of priority. The top value will be used.
silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true
$ bundle config set --local path vendor/bundle
$ bundle config
Settings are listed in order of priority. The top value will be used.
path
Set for your local app (/workspace/sample_app/.bundle/config): "vendor/bundle"

silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true

The problem is when debugging. Imagine a case where the debugger (rdebug-ide) is managed by a Gemfile and installed by bundle install. It should look like launch.json:

json:.vscode/launch.json


{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Local File",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceFolder}/main.rb",
      "useBundler": true
    }
  ]
}

When I run debug, I get an error in DEBUG CONSOLE stating that the required gem cannot be found as shown below.

bundler: failed to load command: rdebug-ide (/usr/local/bundle/bin/rdebug-ide)
Bundler::GemNotFound: Could not find concurrent-ruby-1.1.7 in any of the sources
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler.rb:149:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'

This is because the debug subprocess has BUNDLE_APP_CONFIG =" " in effect, so .bundle / config is not referenced and the path setting is not read.

△ Set the path of .bundle to BUNDLE_APP_CONFIG (Dockerfile)

For example, as shown below, you can specify the absolute path of .bundle for local settings in Dockerfile.

devcontainer/Dockerfile


ENV BUNDLE_APP_CONFIG=/workspace/.bundle

This method works pretty well, but it doesn't work for cases where you have multiple projects in your monorepo (multiple Gemfiles).

△ I set the path of .bundle to BUNDLE_APP_CONFIG (VSCode setting)

For example, the problem that the above debugging does not work can be solved as follows.

json:.vscode/launch.json


{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Local File",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceFolder}/main.rb",
      "useBundler": true,
      "env": {
        "BUNDLE_APP_CONFIG": "${workspaceFolder}/.bundle"
      }
    }
  ]
}

With this method, you can also handle mono-repo cases by setting Multi-root Workspace. However, this setting alone is valid only for the debugging subprocess, so you need to write similar settings in the terminal settings in settings.json and tasks.json as needed. It also makes it difficult to share settings such as launch.json with people who set and use BUNDLE_APP_CONFIG for some reason.

〇 Delete BUNDLE_APP_CONFIG in the devcontainer.json settings

You can unset environment variables instead of emptying or overwriting them by adding the following settings to devcontainer.json.

json:.devcontainer/devcontainer.json


{
  // ...
  "remoteEnv": {
    "BUNDLE_APP_CONFIG": null
  },
  // ...
}

This method removes BUNDLE_APP_CONFIG in the VSCode process and all its subprocesses in the container. Personally, this method seems to have the fewest side effects, so I recommend it.

[^ 1]: mcr.microsoft.com/vscode/devcontainers/ruby is also based on this image [^ 2]: As an aside, the --path option of bundle install has been deprecated ... I recently learned.

Recommended Posts

BUNDLE_APP_CONFIG which is troublesome when developing Ruby with DevContainer
Studying with CodeWar (ruby) ④ case ~ when
Behavior when wild card (**) is specified in ruby