From 148b31fd361a464c9261779e0f98ffacc2132605 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 10 Jul 2020 20:27:12 -0700 Subject: [PATCH] Add explicit bundler:config step to support 2.1+ (#122) * Add explicit bundler:config step to support 2.1+ Starting with Bundler 2.1, the `--path`, `--without`, and `--deployment` options are deprecated. In other words you are no longer supposed to specify these when running `bundle install`. Instead, Bundler wants you to set these options _externally_ from the install command. That way all subsequent invocations of bundler can use the same external configuration without you having to remember which flags to use each time. There are two ways to specify this external configuration: providing environment variables, or running `bundle config`. This commit implements the latter. To summarize, prior to running `bundle check`, `bundle install`, or `bundle clean`, Capistrano will now run a new `bundler:config` task. This task executes the following command as many times as needed: ``` bundle config --local KEY VALUE ``` Each execution sets the external Bundler configuration KEY to VALUE. The following Capistrano variables are automatically consulted to get theses KEYs and VALUEs: - :bundle_gemfile - :bundle_path - :bundle_without This commit also introduces a new variable: - bundle_config It is a Hash that can contain any arbitrary KEY and VALUE pairs to send to `bundle config`. By default it has a single entry: ``` set :bundle_config, { deployment: true } ``` Finally, this commit removes `--deployment` option from the default value of `:bundle_flags`, since that flag is deprecated. It has been replaced by the default `:bundle_config` as mentioned above. * Handle nil :bundle_config --- README.md | 20 +++++------ lib/capistrano/tasks/bundler.cap | 61 +++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f0d7b678..c0826e06 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,13 @@ Configurable options: ```ruby set :bundle_roles, :all # this is default +set :bundle_config, { deployment: true } # this is default set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default set :bundle_binstubs, -> { shared_path.join('bin') } # default: nil set :bundle_gemfile, -> { release_path.join('MyGemfile') } # default: nil -set :bundle_path, -> { shared_path.join('bundle') } # this is default. set it to nil for skipping the --path flag. +set :bundle_path, -> { shared_path.join('bundle') } # this is default. set it to nil to use bundler's default path set :bundle_without, %w{development test}.join(' ') # this is default -set :bundle_flags, '--deployment --quiet' # this is default +set :bundle_flags, '--quiet' # this is default set :bundle_env_variables, {} # this is default set :bundle_clean_options, "" # this is default. Use "--dry-run" if you just want to know what gems would be deleted, without actually deleting them set :bundle_check_before_install, true # default: true. Set this to false to bypass running `bundle check` before executing `bundle install` @@ -96,19 +97,18 @@ To generate binstubs on each deploy, set `:bundle_binstubs` path: set :bundle_binstubs, -> { shared_path.join('bin') } ``` -In the result this would execute the following bundle command on all servers +In the result this would execute the following bundle commands on all servers (actual paths depend on the real deploy directory): ```sh -$ bundle install \ - --binstubs /my_app/shared/bin \ - --gemfile /my_app/releases/20130623094732/MyGemfile \ - --path /my_app/shared/bundle \ - --without development test \ - --deployment --quiet +$ bundle config --local deployment true +$ bundle config --local gemfile /my_app/releases/20130623094732/MyGemfile +$ bundle config --local path /my_app/shared/bundle +$ bundle config --local without "development test" +$ bundle install --quiet --binstubs /my_app/shared/bin ``` -If any option is set to `nil` it will be excluded from the final bundle command. +If any option is set to `nil` it will be excluded from the final bundle commands. If you want to clean up gems after a successful deploy, add `after 'deploy:published', 'bundler:clean'` to config/deploy.rb. diff --git a/lib/capistrano/tasks/bundler.cap b/lib/capistrano/tasks/bundler.cap index 3bb7e7f1..48ecf9da 100644 --- a/lib/capistrano/tasks/bundler.cap +++ b/lib/capistrano/tasks/bundler.cap @@ -1,9 +1,39 @@ +require "shellwords" + namespace :bundler do desc <<-DESC - Install the current Bundler environment. By default, gems will be \ - installed to the shared/bundle path. Gems in the development and \ - test group will not be installed. The install command is executed \ - with the --deployment and --quiet flags. + Configure the Bundler environment for the release so that subequent + `bundle check`, `bundle install`, `bundle clean`, and `bundle exec` + commands all behave consistently. The following settings will be + turned into the appropriate `bundle config` executions: + + :bundle_config + :bundle_gemfile + :bundle_path + :bundle_without + DESC + task :config do + on fetch(:bundle_servers) do + within release_path do + with fetch(:bundle_env_variables) do + configuration = fetch(:bundle_config).dup || {} + configuration[:gemfile] = fetch(:bundle_gemfile) + configuration[:path] = fetch(:bundle_path) + configuration[:without] = fetch(:bundle_without) + + configuration.each do |key, value| + execute :bundle, "config", "--local", key, value.to_s.shellescape unless value.nil? + end + end + end + end + end + + desc <<-DESC + Install the current Bundler environment. By default, gems will be + installed to the shared/bundle path. Gems in the development and + test group will not be installed. The install command is executed + with the --quiet and --jobs=4 flags. By default, bundler will not be run on servers with no_release: true. @@ -11,30 +41,27 @@ namespace :bundler do set :bundle_roles, :all + set :bundle_config, { deployment: true } set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } - set :bundle_binstubs, -> { shared_path.join('bin') } + set :bundle_binstubs, nil set :bundle_gemfile, -> { release_path.join('Gemfile') } set :bundle_path, -> { shared_path.join('bundle') } set :bundle_without, %w{development test}.join(' ') - set :bundle_flags, '--deployment --quiet' - set :bundle_jobs, nil + set :bundle_flags, '--quiet' + set :bundle_jobs, 4 set :bundle_env_variables, {} set :bundle_clean_options, "" DESC - task :install do + task install: :config do on fetch(:bundle_servers) do within release_path do with fetch(:bundle_env_variables) do - options = [] - options << "--gemfile #{fetch(:bundle_gemfile)}" if fetch(:bundle_gemfile) - options << "--path #{fetch(:bundle_path)}" if fetch(:bundle_path) - - if fetch(:bundle_check_before_install) && test(:bundle, :check, *options) + if fetch(:bundle_check_before_install) && test(:bundle, :check) info "The Gemfile's dependencies are satisfied, skipping installation" else + options = [] options << "--binstubs #{fetch(:bundle_binstubs)}" if fetch(:bundle_binstubs) options << "--jobs #{fetch(:bundle_jobs)}" if fetch(:bundle_jobs) - options << "--without #{fetch(:bundle_without)}" if fetch(:bundle_without) options << "#{fetch(:bundle_flags)}" if fetch(:bundle_flags) execute :bundle, :install, *options end @@ -56,7 +83,7 @@ namespace :bundler do end desc "Remove unused gems installed by bundler" - task :clean do + task clean: :config do on fetch(:bundle_servers) do within release_path do with fetch(:bundle_env_variables) do @@ -76,12 +103,14 @@ namespace :load do set :bundle_bins, %w{gem rake rails} set :bundle_roles, :all + + set :bundle_config, { deployment: true } set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } set :bundle_binstubs, nil set :bundle_gemfile, nil set :bundle_path, -> { shared_path.join('bundle') } set :bundle_without, %w{development test}.join(' ') - set :bundle_flags, '--deployment --quiet' + set :bundle_flags, '--quiet' set :bundle_jobs, 4 set :bundle_env_variables, {} set :bundle_clean_options, ""