I setup a series of custom rake tasks for a project that I am building that are name spaced to placewise. These tasks can be viewed by running rake tasks from the console.
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
All of these tasks are stored in lib/tasks/placewise and built like so:
namespace :placewise do
namespace :db do
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = environments(args.env)
msg("Dropping the #{env} database")
shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
msg("Creating the #{env} database")
shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
msg("Running the #{env} database migrations")
shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
end
end
A new task, for example may start with the base setup as follows:
namespace :placewise do
namespace :example do
desc "example"
task :example do
end
end
end
As you can see the namespace :placewise do will be replicated each time. I want to keep all of our custom rake tasks in the same group, however, I am curious if there is a way to avoid having to add that namespace to each .rake file?
Cheers.
Unfortunately I was advised against this strategy and in the process of discovery I found out my helper methods were not setup correctly. So here goes.
I created a new modules folder in lib/modules with the new helper_functions.rb file inside that directory. Here are my helpers:
module HelperFunctions
# ------------------------------------------------------------------------------------
# Permitted environments
# ------------------------------------------------------------------------------------
def environments(arg)
arg = arg || "development"
environments = ["development", "test", "production"]
if environments.include?(arg)
puts
msg("Environment parameter is valid")
return arg
else
error("Invalid environment parameter")
exit
end
end
# ------------------------------------------------------------------------------------
# Console message handler
# ------------------------------------------------------------------------------------
def msg(txt, periods: "yes", new_line: "yes")
txt = txt + "..." if periods == "yes"
puts "===> " + txt
puts if new_line == "yes"
end
def error(reason)
puts "**** ERROR! ABORTING: " + reason + "!"
end
# ------------------------------------------------------------------------------------
# Execute Shell Commands
# ------------------------------------------------------------------------------------
def shell(cmd, step: nil)
msg("Starting step #{step}", new_line: "no") if step.present?
if ENV['TRY']
puts "-->> " + cmd
else
sh %{#{cmd}}
end
msg("#{step} completed!", periods: "no")
end
end
Then in the Rakefile add:
# Shared Ruby functions used in rake tasks
require File.expand_path('../lib/modules/helper_functions', __FILE__)
include HelperFunctions
Rails.application.load_tasks
# Do not add other tasks to this file, make files in the primary lib/tasks dir ending in .rake
# All placewise tasks should be under the lib/tasks/placewise folder and end in .rake
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
And finally my .rake tasks look like:
namespace :placewise do
# ------------------------------------------------------------------------------------
namespace :db do
# ------------------------------------------------------------------------------------
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = HelperFunctions::environments(args.env)
HelperFunctions::msg("Dropping the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
HelperFunctions::msg("Creating the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
HelperFunctions::msg("Running the #{env} database migrations")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
# ------------------------------------------------------------------------------------
end
# ------------------------------------------------------------------------------------
end
Related
I am familiar with Rails but this is my first time uploading to production. I am able to successfully upload my app to AWS and deploy it. However, every time I do that, I have to ssh into my server and run the necessary rake tasks to clean up my models and fully prep my website. Is there a file like production.rb where you can write a script to be run on every production upload. For instance run all tests and rake tests ? Is there a simple example of a script someone. This is the example of my rake file.
Note: I am using AWS Beanstalk, super easy to deploy, just want to run some post production ready scripts.
This is the rake file I want to run commands of post deployment.
require "#{Rails.root}/app/helpers/application_helper"
include ApplicationHelper
namespace :db do
desc "Generate a new blog post markdown"
task new_article: :environment do
cp 'lib/assets/articles/template.md', "lib/assets/articles/NEW_ARTICLE#{Time.now.strftime("%s")}.md"
puts 'new article created!'
end
task populate: :environment do
Article.destroy_all
if User.count == 0
User.create!(name: "AJ", email: "aj#psychowarfare.com")
end
puts Dir.pwd
a = File.join("lib", "assets", "articles", "*.md")
Dir.glob(a).reject { |name| /.*(template|NEW_ARTICLE).*/ =~ name }.each do |file|
File.open(file, "r") do |f|
contents = f.read
mkdown = Metadown.render(contents)
md = mkdown.metadata
unrendered_content = contents.sub(/^---(\n|.)*---/, '')
#puts unrendered_content
article = Article.create!(title: md["title"],
content: markdown(unrendered_content),
header_image: md["header_image"],
published: md["published"],
useful_links: md["useful_links"],
people_mentioned: md["people_mentioned"],
written_at_date: md["written_at_date"],
timestamp: md["timestamp"],
embedded_link: md["embedded_link"],
user: User.first)
article.add_tag(md["tags"])
puts article.useful_links
puts article.people_mentioned
puts article.header_image
puts article.tags
end
end
puts "Article Count: #{Article.count}"
end
end
For post deployment, you can try the following way.
Create a file in .ebextensions/01_build.config
commands:
create_post_dir:
command: "mkdir /opt/elasticbeanstalk/hooks/appdeploy/post"
ignoreErrors: true
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/99_build_app.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
cd /var/app/current/app/
Your-Post-Deploy-Command1
Your-Post-Deploy-Command2
Your-Post-Deploy-Command3
What this config does is:
create the “post” directory if it doesn’t already exist (it won’t by
default) – ignore any errors (such as if the directory already
existed)
deploy the shell script with the appropriate permissions into the right directory
For more details look at the following references: Blog-Article & Stackoverflow-Question
I have deployed a Rails app on an Ubuntu server with Capistrano. However I am trying to run a custom task I had created and is in 'lib/tasks'. I tried to make it work by executing
cap production db:views
As if it was a custom task, but obviously it did not worked
cap aborted!
Don't know how to build task 'db:views'
The file is sql_views.rake, this task is for create views in the database
namespace :db do
desc "Update and create SQL views"
task :views => :environment do
Dir["#{Rails.root}/db/sql_views/*.sql"].each do |file_name|
STDERR.puts "Applying the SQL view at #{file_name}"
source_file = File.new(file_name, 'r')
if source_file and (sql_content = source_file.read)
ActiveRecord::Base.transaction do
# Each statement ends with a semicolon followed by a newline.
sql_lines = sql_content.split(/;[ \t]*$/)
if sql_lines.respond_to?(:each)
sql_lines.each do |line|
ActiveRecord::Base.connection.execute "#{line};"
end
end
end # transaction
end
end # Dir.each
end # task
end
If you used cap install to generate your Capfile, the following lines should be present:
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
So it's looking in lib/capistrano/tasks for custom tasks.
Move lib/tasks/sql_views.rake to lib/capistrano/tasks/sql_views.rake
Or just import each rake task:
import 'lib/tasks/sql_views.rake'
I run task for importing dates to DB Mysql. After running task rake db:restore displayed error:
cannot open %=ENV[C9_USER]%: No such file
What wrong?
task:
require 'yaml'
namespace :db do
def backup_prep
#directory = File.join(Rails.root, 'db', 'backup')
#db = YAML::load( File.open( File.join(Rails.root, 'config', 'database.yml') ) )[ Rails.env ]
#db_params = "-u #{#db['username']} #{#db['database']}"
#db_params = "-p#{#db['password']} #{#db_params}" unless #db['password'].blank?
end
desc 'Backup database by mysqldump'
task :backup => :environment do
backup_prep
FileUtils.mkdir #directory unless File.exists?(#directory)
file = File.join( #directory, "#{RAILS_ENV}_#{DateTime.now.to_s}.sql" )
command = "mysqldump #{#db_params} | gzip > #{file}.gz" #--opt --skip-add-locks
puts "dumping to #{file}..."
# p command
exec command
end
desc "restore most recent mysqldump (from db/backup/*.sql.*) into the current environment's database."
task :restore => :environment do
unless ENV['RAILS_ENV']=='development'
puts "Are you sure you want to import into #{ENV['RAILS_ENV']}?! [y/N]"
return unless STDIN.gets =~ /^y/i
end
backup_prep
wildcard = File.join( #directory, ENV['FILE'] || "#{ENV['FROM']}*.sql*" )
puts file = `ls -t #{wildcard} | head -1`.chomp # default to file, or most recent ENV['FROM'] or just plain most recent
if file =~ /\.gz(ip)?$/
command = "gunzip < #{file} | mysql #{#db_params}"
else
command = "mysql #{#db_params} < #{file}"
end
p command
puts "please wait, this may take a minute or two..."
exec command
end
end
dump db store in path workspace/db/backup/db.sql
Reffering that answer I was trying to use OptionParser to parse rake arguments. I simplified example from there and I had to add two ARGV.shift to make it work.
require 'optparse'
namespace :user do |args|
# Fix I hate to have here
puts "ARGV: #{ARGV}"
ARGV.shift
ARGV.shift
puts "ARGV: #{ARGV}"
desc 'Creates user account with given credentials: rake user:create'
# environment is required to have access to Rails models
task :create => :environment do
options = {}
OptionParser.new(args) do |opts|
opts.banner = "Usage: rake user:create [options]"
opts.on("-u", "--user {username}","Username") { |user| options[:user] = user }
end.parse!
puts "user: #{options[:user]}"
exit 0
end
end
This is the output:
$ rake user:create -- -u foo
ARGV: ["user:create", "--", "-u", "foo"]
ARGV: ["-u", "foo"]
user: foo
I assume ARGV.shift is not the way it should be done. I would like to know why it doesn't work without it and how to fix it in a proper way.
You can use the method OptionParser#order! which returns ARGV without the wrong arguments:
options = {}
o = OptionParser.new
o.banner = "Usage: rake user:create [options]"
o.on("-u NAME", "--user NAME") { |username|
options[:user] = username
}
args = o.order!(ARGV) {}
o.parse!(args)
puts "user: #{options[:user]}"
You can pass args like that: $ rake foo:bar -- '--user=john'
I know this does not strictly answer your question, but did you consider using task arguments?
That would free you having to fiddle with OptionParser and ARGV:
namespace :user do |args|
desc 'Creates user account with given credentials: rake user:create'
task :create, [:username] => :environment do |t, args|
# when called with rake user:create[foo],
# args is now {username: 'foo'} and you can access it with args[:username]
end
end
For more info, see this answer here on SO.
Try this:
require 'optparse'
namespace :programs do |args|
desc "Download whatever"
task :download => [:environment] do
# USAGE: rake programs:download -- rm
#-- Setting options $ rake programs:download -- --rm
options = {}
option_parser = OptionParser.new
option_parser.banner = "Usage: rake programs:download -- rm"
option_parser.on("-r", "--rm","Remove s3 files downloaded") do |value|
options[:remove] = value
end
args = option_parser.order!(ARGV) {}
option_parser.parse!(args)
#-- end
# your code here
end
end
You have to put a '=' between -u and foo:
$ rake user:create -- -u=foo
Instead of:
$ rake user:create -- -u foo
which way, i can run rake commands via capistrano on remote server.
for example i have a lib/task/reparse.rake with some methods
desc "it's take csv file, makes some changes and fill db with this info"
task :example1 => :environment do
require 'csv'
rows_to_insert = []
# some actions
# ...
end
on local server all is fine - i just run rake reparse:example1
and it's work(fill db correctly).
so question is - how can i run this command on real hosting, after deploy?
i'am using rails 4.1 + capistrano 3.
P.S. examples from site not work for me
How do I run a rake task from Capistrano?
if i try cap production rake:invoke task=reparse:land
it fails with:
cap aborted!
Don't know how to build task 'rake:invoke'
some fixes
namespace :somenamespace do
task :runrake do
on roles(:all), in: :sequence, wait: 5 do
within release_path do
execute :rake, ENV['task'], "RAILS_ENV=production"
end
end
end
end
with such way it begin to execute via
cap production somenamespace:runrake task=custom_task_file:custom_method1
Based on the capistrano/rails gem: https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake
namespace :somenamespace do
task :runrake do
on roles(:all) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, ask :task
end
end
end
end
end
You can create a corresponding capistrano task to run a specific rake task like that:
namespace :guests do
desc 'Remove guest users older than 7 days'
task :clean do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, 'guests:delete_old_guest_users'
end
end
end
end
end
So I have been working on this. it seams to work well. However you need a formater to really take advantage of the code.
If you don't want to use a formatter just set the log level to to debug mode.
SSHKit.config.output_verbosity = Logger::DEBUG
Cap Stuff
namespace :invoke do
desc 'Run a bash task on a remote server. cap environment invoke:bash[\'ls -la\'] '
task :bash, :execute do |_task, args|
on primary :app do
within deploy_to do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
execute args[:execute]
end
end
end
end
desc 'Run a rake task on a remote server. cap environment invoke:rake[\'db:migrate\'] '
task :rake, :task do |_task, args|
on primary :app do
within current_path do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
rake args[:task]
end
end
end
end
end
This is the formatter I built to work with the code above. It is based off the :textsimple built into the sshkit but it is not a bad way to invoke custom tasks. Oh this many not works with the newest version of sshkit gem. I know it works with 1.7.1. I say this because the master branch has changed the SSHKit::Command methods that are available.
module SSHKit
module Formatter
class SuperSimple < SSHKit::Formatter::Abstract
def write(obj)
case obj
when SSHKit::Command then write_command(obj)
when SSHKit::LogMessage then write_log_message(obj)
end
end
alias :<< :write
private
def write_command(command)
unless command.started? && SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Running #{String(command)} #{command.host.user ? "as #{command.host.user}#" : "on "}#{command.host}\n"
if SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Command: #{command.to_command}" + "\n"
end
end
unless command.stdout.empty?
command.stdout.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
unless command.stderr.empty?
command.stderr.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
end
def write_log_message(log_message)
original_output << log_message.to_s + "\n"
end
end
end
end
you need to load a custom rake task in Capistrano config:
# config/deploy.rb || config/deploy/production.rb
load 'lib/task/reparse.rake'
check for new task in console cap -T
Try capistrano-rake
Without messing with custom capistrano tasks or going into much detail, all you need to do is simply install the gem and you can start executing rake tasks on remote servers like this:
$ cap production invoke:rake TASK=some:rake_task