Capistrano Deploy, access ActiveRecord - ruby

Okay,
I'm sorry if the title is not descriptive enough, but allow me to explain what I want to achieve:
I have a Rails 3 application
During my deploy, it needs to call pg_dump with the correct parameters to restore a backup
The task needs to be ran after the deploy is done but before the migrations.
The problem I have however, is that for this task, I would like to access Rails specific code, which is not working as Capistrano keeps throwing a lot of errors at me like gems not available or module not defined.
This is my Rake task:
namespace :deploy do
namespace :rm do
desc 'Backup the database'
task :backup do
# Generates the command to invoke the Rails runner
# Used by the cfg method to execute the ActiveRecord configuration in the rails config.
def runner
dir = "#{fetch(:deploy_to)}/current"
bundler = "#{SSHKit.config.command_map.prefix[:bundle].first} bundle exec"
env = fetch(:rails_env)
"cd #{dir}; #{bundler} rails r -e #{env}"
end
def cfg(name)
env = fetch(:rails_env)
command = "\"puts ActiveRecord::Base.configurations['#{env}']['#{name}']\""
"#{runner} #{command}"
end
on roles(:db) do
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
backups = File.expand_path(File.join(fetch(:deploy_to), '..', 'backups'))
execute :mkdir, '-p', backups
dump = "PGPASSWORD=`#{cfg('password')}` pg_dump -h `#{cfg'host')}` -U `#{cfg('username')}` `#{cfg('database')}`"
fn = "#{timestamp}_#{fetch(:stage)}.sql.gz"
path = File.join(backups, fn)
execute "#{dump} | gzip > #{path}"
end
end
end
end
In it's current form, it simply generates a string with the runner method and dumps that inside the cfg method.
I tried rewriting the runner method, but for some reason I keep getting the runner --help output from the remote server, but the command being generated in the output is correct, and works locally just fine.
We are using Ruby 2.2.2 and RVM on the remote server.
Is it even possible to do what we are trying to construct together?

I'd suggest writing a rake task inside your Rails app and invoking that from your Capistrano task. This is how the Capistrano rails tasks work.
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:migrate"
end
end

Related

capistrano aborts - SSHKit::Runner::ExecuteError and Devise.secret_key was not set

I'm trying to update an app (Rails 4.1.12; ruby 1.9.3p484) by deploying changes from development to production using capistrano. Below is log (I masked the IP address and suggested secret key).
Previously never had a problem running a deploy. Have searched and found posts with similar errors but have not found a solution. I did follow the instructions to add Devise.secret_key to the Devise initializer and restarted the application (dev and prod). Next attempt to deploy received same error with suggestion for different secret key.
All help, hints, suggestions are greatly appreciated. Thanks.
UPDATE:I just tried to deploy using
bundle exec cap production deploy
and did not get errors but still get errors when running
cap production deploy
UPDATE 2: I used Figaro to generate an application.yml file (and append to gitignore). In the devise.rb file I created a line config.secret_key = ENV["config.secret_key"] In the application.yml file I created a line config.secret_key: 'random_128_chr_string' I then restarted the development server and ran cap production deploy Unfortunately got same error.
cap production deploy
.
.
.
[deploy:migrate] Run `rake db:migrate`
[e0c2c251] Running bundle exec rake db:migrate as deployer#123.123.123.12
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deployer#123.123.123.12: rake exit status: 1
rake stdout: Nothing written
rake stderr: rake aborted!
Devise.secret_key was not set. Please add the following to your Devise initializer:
config.secret_key = 'looong_string_of_numbers_and_letters'
Please ensure you restarted your application after installing Devise or setting the key.
deploy.rb:
lock '>=3.2.1'
set :application, 'pepperslice'
set :repo_url, 'git#github.com:jeffaltman/pepperslice.git'
set :linked_dirs, %w{log tmp public/images/uploaded}
set :linked_files, %w{config/database.yml config/initializers/devise.rb config/initializers/smtp_settings.rb}
set :format, :pretty
set :log_level, :info
namespace :deploy do
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
execute "sudo /usr/sbin/service unicorn restart"
end
end
after :publishing, :restart
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
end
capfile:
# Load DSL and Setup Up Stages
require 'capistrano/setup'
# Includes default deployment tasks
require 'capistrano/deploy'
# Includes tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
#
# require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler'
# require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
#require 'capistrano/ssh_doctor'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Mina deployment : invoke task once `current` symlink is updated

I'm using Mina (a simpler alternative to Capistrano) to deploy my ruby websites, and I am trying to run some tasks once the current symlink has been updated.
So far, here's what I have in my deploy.rb file:
desc "Deploys the current version to the server."
task :deploy => :environment do
deploy do
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
invoke :'bundle:install'
to :launch do
invoke :restart
end
end
end
desc "Manually restart Thin web server"
task :restart do
in_directory "#{deploy_to}/current" do
queue! %[bundle exec thin restart -C "#{thin_yml}"]
end
end
My problem is that when Mina hits the to :launch block, the current symlink has not yet been updated, so either it does not exist (if it is the 1st deployment for this project) or it's still pointing to the n-1 release (and thus, the server uses an outdated version of the project).
So I'd like to be able to invoke my :restart task once the new release has been moved to the release directory and the current symlink has been updated.
I think it's a bug of Mina. in_directory seems to not work properly when used inside a to context. A quick and dirty workaround would be adding #commands[:default] = commands(#to) at the end of the in_directory block.
desc "Manually restart Thin web server"
task :restart do
in_directory "#{deploy_to}/current" do
queue! %[bundle exec thin restart -C "#{thin_yml}"]
#commands[:default] = commands(#to)
end
end

How can I load ActiveRecord database tasks on a Ruby project outside Rails?

ActiveRecord 3.2.14
I want to use ActiveRecord in a non-Rails Ruby project. I want to have available the rake tasks that are defined by ActiveRecord. How can I do that?
rake db:create # Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)
rake db:drop # Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)
rake db:fixtures:load # Load fixtures into the current environment's database
rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false)
rake db:migrate:status # Display status of migrations
rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR
rake db:schema:load # Load a schema.rb file into the database
rake db:seed # Load the seed data from db/seeds.rb
rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)
rake db:structure:dump # Dump the database structure to db/structure.sql
rake db:version # Retrieves the current schema version number
The above list is the list of tasks that I want to be able to use on my non-Rails Ruby project that uses ActiveRecord. What do I have to write in my Rakefile?
Thanks in advance
The easiest thing to do is to load the tasks already defined in databases.rake. Here is a GIST of how it was done.
Inspired by this GIST by Drogus
Rakefile.rb
require 'yaml'
require 'logger'
require 'active_record'
include ActiveRecord::Tasks
class Seeder
def initialize(seed_file)
#seed_file = seed_file
end
def load_seed
raise "Seed file '#{#seed_file}' does not exist" unless File.file?(#seed_file)
load #seed_file
end
end
root = File.expand_path '..', __FILE__
DatabaseTasks.env = ENV['ENV'] || 'development'
DatabaseTasks.database_configuration = YAML.load(File.read(File.join(root, 'config/database.yml')))
DatabaseTasks.db_dir = File.join root, 'db'
DatabaseTasks.fixtures_path = File.join root, 'test/fixtures'
DatabaseTasks.migrations_paths = [File.join(root, 'db/migrate')]
DatabaseTasks.seed_loader = Seeder.new File.join root, 'db/seeds.rb'
DatabaseTasks.root = root
task :environment do
ActiveRecord::Base.configurations = DatabaseTasks.database_configuration
ActiveRecord::Base.establish_connection DatabaseTasks.env.to_sym
end
load 'active_record/railties/databases.rake'
You could try the standalone-migrations gem:
https://github.com/thuss/standalone-migrations
For Rails 3.x:
You need to manually create the tasks. As example here is how to add them (this example uses the environment variables like Rails):
namespace :db do
desc "Drop and create the current database"
task :recreate => :environment do
abcs = ActiveRecord::Base.configurations
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
ActiveRecord::Base.connection.recreate_database(ActiveRecord::Base.connection.current_database)
end
end
and you'll have the task rake db:recreate available
For Rails 4.x:
If you want to have the ActiveRecord rake tasks available in your ruby app, take a look at the documentation.
Example usage of DatabaseTasks outside Rails could look as such:
include ActiveRecord::Tasks
DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
DatabaseTasks.db_dir = 'db'
# other settings...
DatabaseTasks.create_current('production')
Also you have here an example on how to use ActiveRecord in your ruby aplication.
Create your own!
Reference the Rails one though:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/railties/databases.rake
Create a Rake Task file. To use Rake, generally you want a tasks folder filled with Rake task files. These files have the ".task" extension.
Study the file to link given.
Take parts of that file, or even the entire contents of the file, and add it to your new Rake task file.
Make sure your Rakefile loads those task files. Your Rakefile should have something like this
-
Dir[File.join(PROJECT_ROOT, 'tasks', '**', '*.rake')].each do |file|
load file
end
I believe you can use the sinatra-activerecord gem even if you're not using Sinatra. I just solved this problem by requiring that gem and then adding
require 'sinatra/activerecord/rake'
to my rakefile.
Once I added that require line the db tasks showed up in my rake -T!
If you are using Sinatra, you can use this gem:
https://github.com/janko-m/sinatra-activerecord
However, if you don't use it either, the source code inside provides a good example on how to implement AR rake tasks.

How to get the current Rack environment in Rake?

Is there a way to get information about the current Rack environment in Rake? For example, how can I tell whether Rack is running in development or production mode?
I understand that Rake is not Rack-aware. I'm trying to avoid replicating code in nearly-identical Rake tasks between production and dev environments.
Question is old but never fetched the best practice answer or a satisfying answer at all.
The real question is: How to go sure which environment is used in a Rake task in order to load the correct configuration / hitting into correct if-conditions.
Note: As Rake doesn't give much about Rack (Rake is not using HTTP) to
rely on the RACK_ENV is basically wrong but common and handy if a Rake
task loads your main Sinatra application (the RACK_ENV is required to let
Sinatras development? / test? / production? being set correctly).
The answer: Set the environment with each Rake task call.
Command line call:
/usr/bin/rake namespace:task_name RACK_ENV=production
Cronjob call (in crontab):
cd /into/your/app/root && /usr/bin/rake namespace:task_name RACK_ENV=production --silent
Note: To specify the path of the Rake bin is not necessary if you have it in your global system variables. Your path might differs from mine used in the examples, check on Unix systems with: whereis rake
You can check the RACK_ENV in your tasks via:
puts ENV["RACK_ENV"]
As other environment variable, you can retrieve it using:
ENV['RACK_ENV']
Considering it's a Sinatra application, and that you've set the environment into config/environment.rb, you can add the following to your Rakefile:
task :environment do
require File.expand_path('config/environment', File.dirname(__FILE__))
end
task :your_task => :environment do
# task
end
Then, you can retrieve the environment (depending how you set it up in your environment.rb) with ENV['RACK_ENV'] or Sinatra::Application.environment.
Considering there isn't a config/environment.rb config file, only the application file, for instance hello_world.rb, the following works:
hello_world.rb:
require 'sinatra'
set :environment, :production
get '/' do
'Hello World'
end
Rakefile:
task :environment do
require File.expand_path('hello_world', File.dirname(__FILE__)) # your Sinatra app
end
task :your_task => :environment do
puts Sinatra::Application.environment
end
When doing rake your_task you should obtain:
> rake your_task
production
After 2.5 years, I want to share what I've found to be the best solution.
Create a .env file in the root folder of the application, and add a flag specifying the application environment:
ENVIRONMENT=development
Then use Brandon Keepers' dotenv gem to load all environment variables from this file. Now you can use any environment variables specified in .env within Rake tasks.
Rake will rely on the explicit value set in .env, so you must create separate .env files for each environment you plan on using (e.g. dev, test, staging, production, etc).
Sample Rakefile:
require 'dotenv/tasks'
task :default => :help
desc 'Show this help menu'
task :help do
puts "Available rake tasks:"
system('rake --tasks')
end
# Will run in any environment
desc 'Demo task'
task :demo_task => :dotenv do
puts "Running demo task in '#{ENV['ENVIRONMENT']}' mode"
end
# Will only run if ENVIRONMENT value in .env file is set to 'production'
desc 'Production-only task'
task :production_task => :dotenv do
if ENV['ENVIRONMENT'] == 'production'
puts "Running 'Production-only' task"
else
puts "Won't run, because the environment is not set to PRODUCTION!"
end
end
# Will only run if ENVIRONMENT value in .env file is set to 'development'
desc 'Development-only task'
task :dev_task => :dotenv do
if ENV['ENVIRONMENT'] == 'development'
puts "Running 'Development-only' task"
else
puts "Won't run, because the environment is not set to DEVELOPMENT!"
end
end
If you want to use the environment variables within your Rack or Sinatra app (which you probably do), add the following to the application's config or bootstrap block:
require 'dotenv'
Dotenv.load

Running RSpec Files From ruby code

I'm trying to run RSpec tests straight from ruby code. More specifically, I'm running some mysql scripts, loading the rails test environment and then I want to run my rspec tests (which is what I'm having trouble with)... I'm trying to do this with a rake task. Here is my code so far:
require "spec/autorun"
require"spec"
require "spec/rake/spectask"
RAILS_ENV = 'test'
namespace :run_all_tests do
desc "Run all of your tests"
puts "Reseting test database..."
system "mysql --user=root --password=dev < C:\\Brian\\Work\\Personal\\BrianSite\\database\\BrianSite_test_CreateScript.sql"
puts "Filling database tables with test data..."
system "mysql --user=root --password=dev < C:\\Brian\\Work\\Personal\\BrianSite\\database\\Fill_Test_Tables.sql"
puts "Starting rails test environment..."
task :run => :environment do
puts "RAILS_ENV is #{RAILS_ENV}"
# Run rspec test files here...
require "spec/models/blog_spec.rb"
end
end
I thought the require "spec/models/blog_spec.rb" would do it, but the tests aren't running. Anyone know where I'm going wrong?
UPDATE: I've added the require "spec/autorun" command at the top of the file and now I am running into this error when I do a rake run_all_tests:run :
C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner/options.rb:283:in fi
les_to_load': File or directory not found: run_all_tests:run (RuntimeError)
from C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner/options.
rb:275:ineach'
from C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner/options.
rb:275:in files_to_load'
from C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner/options.
rb:133:inrun_examples'
from C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner.rb:61:in
run'
from C:/Ruby/lib/ruby/gems/1.8/gems/rspec-1.3.0/lib/spec/runner.rb:45:in
autorun'
from C:/Ruby/bin/rake:19
It's hitting this error when it gets to the require "spec/models/blog_spec.rb" line. This file does exist because when I try and change the require statement, I just get a file not found error. It seems like rspec is trying to now run the tests, but is running into problems... any thoughts?
Thanks for any help.
Try adding require "spec/autorun to the top of your file.
You don't need to do it that way though, because there are built in Rake tasks (that's what the spec/rake/spectask is including) to do what you're doing: http://rspec.info/documentation/tools/rake.html

Resources