Capistrano: deploy.rb file refactoring - ruby-on-rails

I have following code in my deploy.rb
namespace :app do
desc "copies the configuration frile from ~/shared/config/*.yml to ~/config"
task :copy_config_files,:roles => :app do
run "cp -fv #{deploy_to}/shared/config/hoptoad.rb #{release_path}/config/initializers"
run "cp -fv #{deploy_to}/shared/config/app_config.yml #{release_path}/config/app_config.yml"
end
end
I thought it would be a good idea to keep my deploy.rb file clean and I attempted to move above code to capistrano_utilities.rb under config. I am using Rails application. And I added following line of code to deploy.rb
require File.expand_path(File.dirname(__FILE__) + "/../lib/capistrano_utilities")
Now I am getting following error.
undefined method `namespace' for main:Object (NoMethodError)
The value of self in the deploy.rb is Capistrano::Configuration . While the value of self in capistrano_utilities is Main. So I understand why I am getting namespace method error. What is the fix for this problem?

In your config/deploy.rb, try load instead of require. Also, capistrano already runs as if you're at the RAILS_ROOT, so there's no need to use __FILE__:
load "lib/capistrano_utilities"
In a capistrano config file, load is redefined to load another configuration file into the current configuration. When passing a path to it, it actually calls load_from_file (a private method defined by capistrano) that just reads the file from disk and instance_eval's it.

Check your Capfile on Rails.root.
if you use capistrano 3, you see this line;
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
Now, put your file on "lib/capistrano/tasks/capistrano_utilities.cap" and it will be loaded.

Related

How can I use Rails 5.2 credentials in capistrano's deploy.rb file?

I've just updated my Rails app to 5.2, and configured it to use the new config/credentials.yml.enc file.
When I try to deploy, I get this error:
NameError: uninitialized constant Rails
/Users/me/Documents/project/config/deploy.rb:27:in `<top (required)>'
That's pointing to this line in my config/deploy.rb file:
set :rollbar_token, Rails.application.credentials[:rollbar_token]
So it appears that while capistrano is running, it doesn't have access to Rails.application.credentials.
How are you all handling this? I've got some ideas...
Set this one variable as an ENV variable
I don't love how this separates/customizes this one setting
Somehow make it so capistrano has access to Rails.application.credentials
I don't know if this is a good idea or if there are other things I need to be aware of if I go this route
Remove deploy tracking in rollbar
🤷‍♂️
Put the following line(s) on top of your config/deploy.rb
# config/deploy.rb
require File.expand_path("./environment", __dir__)
This include make constants like Rails.application accessible in files like config/deploy/production.rb. Now things like the following are possible:
# config/deploy/staging.rb
server "production.lan", user: "production", roles: %w{app db web}
set :stage, :production
set :branch, "development"
set :pg_password, Rails.application.credentials[:staging][:postgres][:password]
I solved the problem as follows:
set :rollbar_token, YAML.load(`rails credentials:show`)['rollbar_token']
1. Upload master.key the file on the server (user read-only) like so:
namespace :setup do
desc "setup: copy config/master.key to shared/config"
task :copy_linked_master_key do
on roles(fetch(:setup_roles)) do
sudo :mkdir, "-pv", shared_path
upload! "config/master.key", "#{shared_path}/config/master.key"
sudo :chmod, "600", "#{shared_path}/config/master.key"
end
end
before "deploy:symlink:linked_files", "setup:copy_linked_master_key"
end
Put it in your lib/capistrano/tasks/setup.rake
2. Ensure file is linked
In deploy.rb:
set :linked_files, fetch(:linked_files, []).push("config/master.key")
3. Ensure Capfile loads the task:
Make sure your Capfile has the line
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
require File.expand_path("./environment", __dir__)
puts App::Application.credentials.rollbar_token
The way I solve this is to declare a $ROLLBAR_ACCESS_TOKEN environment variable on the server. I place it at the top of ~deployer/.bashrc like this:
export ROLLBAR_ACCESS_TOKEN=...
Then I integrate with Capistrano by defining this task:
task :set_rollbar_token do
on release_roles(:all).first do
set :rollbar_token, capture("echo $ROLLBAR_ACCESS_TOKEN").chomp
end
end
before "rollbar:deploy", "set_rollbar_token"
It seemed to me non-ideal to load the whole of Rails here, or to have to read command line output, so here's an alternative solution:
require "active_support/encrypted_configuration"
require "active_support/core_ext/hash/keys"
module CredentialLoader
def read_credentials(environment:)
YAML.load(
ActiveSupport::EncryptedConfiguration.new(
config_path: "config/credentials/#{environment}.yml.enc",
key_path: "config/credentials/#{environment}.key",
env_key: environment.to_s,
raise_if_missing_key: true
).read
)
end
end
Then you can do this in your deploy.rb:
include CredentialLoader
set :rollbar_token, read_credentials(environment: fetch(:stage))["rollbar_access_token"]

Rails: create symlink with capistrano before initializer from config/initializers is executed

I have a capistrano task that is executed like this in deploy.rb:
after 'deploy:update_code', 'deploy:create_symlink'
The task:
Capistrano::Configuration.instance.load do
namespace :deploy do
task :create_symlink do
run "touch #{shared_path}/somefile.yml"
run "ln -nfs #{shared_path}/somefile.yml #{release_path}/config/somefile.yml"
end
end
end
configs are loaded from somefile.yml like this:
customconf = OpenStruct.new(YAML::load_file(File.join(Rails.root, 'config', 'somefile.yml'))[Rails.env]||{})
The issue that I'm having is that the configs are loaded in config/initializers/customconfig.rb. but the symlink seems to be created after the code in customconfig.rb is created.
This is the error I'm getting when trying to cap deploy:
Errno::ENOENT: No such file or directory - /var/www/vhosts/mysite/rails/releases/20170705083649/config/somefile.yml
Basically how can I load the configs from somefile.yml after the symlink is created. Or how can I run the cap task before the initializer is executed?
I solved my issue by changing after 'deploy:update_code' to after after 'deploy:finalize_update'. But I want to ask if this is the correct way of doing it before I accept this as accepted answer.

rake aborted! Don't know how to build task 'sandbox'

I'm trying to add the sandbox to my rails spree application and have run into this error
(using windows 8/powershell with Rails 4.1.6). I'm going by this manual: https://github.com/spree/spree/issues/411
This link Use older version of Rake
seems to have a similar issue but I am not sure how to take the necessary steps to achieve it.
When I try:
C:\Ruby193\openUpShop> bundle exec rake sandbox
I get:
rake aborted!
Don't know how to build task 'sandbox'
I'm am new to rails and am still not sure how everything works so a throughout explanation
with step by step instructions would be greatly appreciated! Thanks.
you can use a file sandbox.rb
# use example: rake task:sub_task -- --sandbox
if ARGV.any? {|arg| arg == '--sandbox' }
puts "** << USING SANDBOX!! >> **"
# beginning
$sandbox = -> do
ActiveRecord::Base.connection.begin_transaction
end
# end
at_exit do
ActiveRecord::Base.connection.rollback_transaction
end
end
then only you need add at the beginning of your task.rake file
require_relative 'path_to_your/sandbox.rb'
..and add at the beggining of your task code
desc "description task"
task example_task: :environment do
$sandbox.call if $sandbox.present?
...

Can you make folder with rake build?

I have no experience with Ruby or rake or anything, but I am using slate for API documentation, and it uses Ruby and rake and stuff to build the file. I know nothing at all about these things, but what I do know is this: when I do a rake build it updates a folder (slate/build). I then have to manually copy slate/build to ../app/docs after every single rake build. Is there something I can do that will copy that folder on every rake build automatically for me?
Add to your Rakefile:
ROOT = File.expand_path('..', __FILE__)
task :build_and_move => [:build] do
cp_r(File.join(ROOT, 'slate/build'), File.join(ROOT, '../app/docs'))
# or
# mv(File.join(ROOT, 'slate/build'), File.join(ROOT, '../app/docs'))
end
and then run rake build_and_move.
You can use FileUtils for this.
Docs: http://ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html#method-c-copy
Example from the docs:
Copies src to dest. If src is a directory, this method copies all its contents recursively. If dest is a directory, copies src to dest/src.
FileUtils.cp 'eval.c', 'eval.c.org'
FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink

How can I tell if Rails code is being run via rake or script/generate?

I've got a plugin that is a bit heavy-weight. (Bullet, configured with Growl notifications.) I'd like to not enable it if I'm just running a rake task or a generator, since it's not useful in those situations. Is there any way to tell if that's the case?
It's as simple as that:
if $rails_rake_task
puts 'Guess what, I`m running from Rake'
else
puts 'No; this is not a Rake task'
end
Rails 4+
Instead of $rails_rake_task, use:
File.basename($0) == 'rake'
I like NickMervin's answer better, because it does not depend on the internal implementation of Rake (e.g. on Rake's global variable).
This is even better - no regexp needed
File.split($0).last == 'rake'
File.split() is needed, because somebody could start rake with it's full path, e.g.:
/usr/local/bin/rake taskname
$0 holds the current ruby program being run, so this should work:
$0 =~ /rake$/
It appears that running rake will define a global variable $rakefile, but in my case it gets set to nil; so you're better off just checking if $rakefile has been defined... seeing as __FILE__ and $FILENAME don't get defined to anything special.
$ cat test.rb
puts(global_variables.include? "$rakefile")
puts __FILE__
puts $FILENAME
$ cat Rakefile
task :default do
load 'test.rb'
end
$ ruby test.rb
false
test.rb
-
$ rake
(in /tmp)
true
./test.rb
-
Not sure about script/generator, though.
The most stable option is to add $is_rake = true at the beginning of Rakefile and use it from your code.
Use of $0 or $PROGRAM_NAME sometimes will fail, for example when using spring and checking variables from config/initializers
You can disable the plugin using environment variable:
$ DISABLE_BULLET= 1 rake some:task
And then in your code:
unless ENV['DISABLE_BULLET']
end
We could ask this
Rake.application.top_level_tasks
In a rails application, this is an empty array, whereas in a Rake task, the array has the task name in it.
top_level_tasks probably isn't a public API, so it's subject to changes. But this is the only thing I have found.

Resources