1

I've added the webpacker gem to my Rails 5.2 app, and now I'm trying to deploy it to a server with Capistrano. The process fails during the deploy:assets:precompile step with the following error message:

DEBUG [f2c62805] Command: cd /var/www/myapp/releases/20200805023716 && ( export RAILS_ENV="production" RAILS_GROUPS="" ; /usr/local/rvm/bin/rvm 2.5.7 do bundle exec rake assets:precompile )

 DEBUG [f2c62805]       Compiling...

 DEBUG [f2c62805]       Compilation failed:

webpack config /var/www/myapp/shared/config/webpack/production.js not found, please run 'bundle exec rails webpacker:install' to install Webpacker with default configs or add the missing config file for your custom environment.

I don't know why it's looking in the shared/config folder rather than the new release's folder. Presumably I wouldn't want my config to be shared in case I change it and future deploy fails. In that case, the current version of my app would have config that may not be suitable for it.

Here's some of the relevant Capistrano configuration:

set :config_files, ['config/boot.rb', 'config/database.yml', 'config/secrets.yml']
set :bin_files, ['bin/bundle', 'bin/delayed_job', 'bin/rails', 'bin/rake', 'bin/webpack']

# Tells Capistrano to store config/database.yml file inside a directory called /shared, which is meant for any files
# we want to persist between deploys
set :linked_files, fetch(:linked_files, []) + fetch(:config_files)

# Directories that are meant to persist between deploys, and they will also be stored inside /shared
set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

Sprockets assets are compiled just fine. I've tried running bundle exec rake assets:precompile on the server, and it does look in the shared/config folder. I ran a --trace and found out that this runs the webpacker:compile step, which is where it fails.

What can I do to make it look for the config file in the current release's directory (/var/www/myapp/releases/20200805023716/config/webpack/production.js)?

Alexander
  • 3,959
  • 2
  • 31
  • 58

1 Answers1

1

I fixed it! There were two problems:

  1. The bin files in the shared directory
  2. Not installing yarn packages before precompiling assets

Problem #1: The bin files in the shared directory

I had previously set up my deployment to copy the bin files to the shared directory so that delayed_job could run. Uploading the files automatically set these files as executables. Up until now, it hasn't been a problem. But it's a problem when compiling webpack assets because of this line in Webpacker's compilation process:

stdout, stderr, status = Open3.capture3(
  webpack_env,
  "#{RbConfig.ruby} ./bin/webpack",
  chdir: File.expand_path(config.root_path)
)

Since I moved all of my bin files to the shared directory and Capistrano set up a symlink, it would run the bin/webpack file in the shared directory. When that command runs, it looks for relative paths to the Webpacker config files. Given the context, this means it would look for my webpack config files in /var/www/myapp/shared/config/webpack/production.js instead of /var/www/myapp/releases/20200805023716/config/webpack/production.js.

Solution: Stop copying bin files to the shared directory, and set them to executable

To fix this, I removed this line from the Capistrano deploy script:

# config/deploy.rb
set :bin_files, ['bin/bundle', 'bin/delayed_job', 'bin/rails', 'bin/rake', 'bin/webpack']

Then I removed bin from my linked directories:

# config/deploy.rb
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

After this, Webpacker would look in the correct directory when precompiling assets. Now, I needed to fix the executable problem. I added a new Capistrano task, which I found on this SO answer:

before "deploy:symlink:release", "deploy:ensure_bin_files_executable"

namespace :deploy do
  desc 'Ensure that bin files are executable'
  task :ensure_bin_files_executable do
    on roles(:web) do
      within release_path do
        execute "cd #{release_path} && chmod +x bin/*"
      end
    end
  end
end

This made all of the files in the bin/ folder for the current release executable. Problem solved!

Problem #2: Not installing yarn packages before precompiling assets

After solving the first problem, I received this error when precompiling assets:

01:33 deploy:assets:precompile
  01 /usr/local/rvm/bin/rvm default do bundle exec rake assets:precompile
  01 Webpacker is installed  
  01 Using /var/www/myapp/releases/20200805023716/config/webpacker.yml file for setting up webpack paths
  01 Compiling…
  01 Compilation failed:
  01 yarn run v1.13.0
  01 info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
  01
  01 warning package.json: No license field
  01 error Command "webpack" not found.
Solution: Install yarn packages before precompiling assets

I realized that Yarn had not installed the webpack package, so I found this helpful Capistrano task on the Webpacker Deployment document:

Make sure you have public/packs and node_modules in :linked_dirs

append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/packs", ".bundle", "node_modules"

If you have node_modules added to :linked_dirs you'll need to run yarn install before deploy:assets:precompile, so you can add this code snippet at the bottom deploy.rb

before "deploy:assets:precompile", "deploy:yarn_install"

namespace :deploy do
  desc "Run rake yarn install"
  task :yarn_install do
    on roles(:web) do
      within release_path do
        execute("cd #{release_path} && yarn install --silent --no-progress --no-audit --no-optional")
      end
    end
  end
end

Once I added it, my deployment went smoothly!

Alexander
  • 3,959
  • 2
  • 31
  • 58