# based on capistrano-faster-assets gem
class WebpackedBuildRequired < StandardError; end
namespace :deploy do
namespace :webpacked do OPTIONS = lambda do |option| { dependencies: fetch(:webpacked_dependencies) || %w(frontend npm-shrinkwrap.json), manifest_path: fetch(:webpacked_manifest_path) || 'webpack-assets.json', deploy_manifest_path: fetch(:webpacked_local_manifest_path) || 'webpack-assets-deploy.json', local_output_path: fetch(:webpacked_local_output_path) || "public/#{fetch(:assets_prefix)}/webpack", release_output_path: fetch(:webpacked_release_output_path) || "public/#{fetch(:assets_prefix)}/webpack" }.fetch(option) end desc 'Webpack build assets' task :build do on roles(fetch(:assets_roles)) do with rails_env: fetch(:rails_env) do begin latest_release = capture(:ls, '-xr', releases_path).split[1] raise WebpackedBuildRequired unless latest_release latest_release_path = releases_path.join(latest_release) OPTIONS.(:dependencies).each do |dep| release = release_path.join(dep) latest = latest_release_path.join(dep) # skip if both directories/files do not exist next if [release, latest].map { |d| test "test -e #{d}" }.uniq == [false] # execute raises if there is a diff begin execute(:diff, '-Nqr', release, latest) rescue SSHKit::Command::Failed raise WebpackedBuildRequired end end latest_manifest_path = latest_release_path.join(OPTIONS.(:manifest_path)) unless test("[[ -f #{latest_manifest_path} ]]") info "Latest webpack manifest not found: #{latest_manifest_path}. Need rebuild" raise WebpackedBuildRequired end info 'Skipping webpack build, no diff found' execute( :cp, latest_release_path.join(OPTIONS.(:manifest_path)), release_path.join(OPTIONS.(:manifest_path)) ) rescue WebpackedBuildRequired invoke 'deploy:webpacked:build_force' end end end end task :build_force do run_locally do info 'Create webpack local build' `RAILS_ENV=#{fetch(:rails_env)} npm run build:production` invoke 'deploy:webpacked:sync' end end desc 'Sync locally compiled assets with current release path' task :sync do on roles(fetch(:assets_roles)) do upload!( OPTIONS.(:deploy_manifest_path), release_path.join(OPTIONS.(:manifest_path)) ) execute(:mkdir, '-p', shared_path.join(OPTIONS.(:release_output_path))) end roles(fetch(:assets_roles)).each do |host| run_locally do info 'Sync assets...' local_output_path = OPTIONS.(:local_output_path) release_output_path = shared_path.join(OPTIONS.(:release_output_path)) `rsync -avzr --delete #{local_output_path} #{host.user}@#{host.hostname}:#{release_output_path.parent}` end end end end
end