Always run rake task when uploading to production - ruby-on-rails

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

Related

Running rake task on remote app, use file from local machine

How can I pass the csv file or file stream or something in line of that to the rake task I'm running on the remote app via rake task arguments?
So I can get the contents of that file in the file and do something with it. It's not a big file.
Update
I tried with suggestion from Luc:
desc 'Test task'
namespace :app do
task :pipe_file => [:environment] do |t, args|
puts "START"
File.open('my_temp_file', 'w') do |f2|
while line = STDIN.gets
f2.puts line
end
end
puts "DONE"
end
end
So when I run :
cat tst.csv | bundle exec rake app:pipe_file
Nothing happens, blank line prints
You can pipe the content of your file to your rake task:
cat my_file | heroku run rake --no-tty my_task
Then inside your task you need to start by reading STDIN:
STDIN.binmode
tmp_file = Tempfile.new('temp_file_prefix', Rails.root.join('tmp'))
tmp_file.write(STDIN.read)
tmp_file.close
Process tmp_file here.
puts tmp_file.path
tmp_file.unlink
Hope it helps !

How to run custom rake task via capistrano 3?

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

rake tasks dry up namespace with namespace

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

Adding a custom seed file

I want to populate a new feature with dummy data, but don't want to use the db/seeds.rb file as it already has seeds other data irrelevant for this feature.
To run the default seeds.rb file, you run the command rake db:seed.
If I create a file in the db directory called seeds_feature_x.rb, what would the rake command look like to run (only) that file?
Start by creating a separate directory to hold your custom seeds – this example uses db/seeds. Then, create a custom task by adding a rakefile to your lib/tasks directory:
# lib/tasks/custom_seed.rake
namespace :db do
namespace :seed do
Dir[Rails.root.join('db', 'seeds', '*.rb')].each do |filename|
task_name = File.basename(filename, '.rb')
desc "Seed " + task_name + ", based on the file with the same name in `db/seeds/*.rb`"
task task_name.to_sym => :environment do
load(filename) if File.exist?(filename)
end
end
end
end
This rakefile accepts the name of a seed file in the db/seeds directory (excluding the .rb extension), then runs it as it would run seeds.rb. You can execute the rake task by issuing the following from the command line:
rake db:seed:file_name # Name of the file EXCLUDING the .rb extension
Update: Now it should also list the seed tasks when running rake --tasks or rake -T.
I tried out zeantsoi's answer but it didn't give me what I wanted, it did all files in a directory. Hacked away at it and got this.
namespace :db do
namespace :seed do
task :single => :environment do
filename = Dir[File.join(Rails.root, 'db', 'seeds', "#{ENV['SEED']}.seeds.rb")][0]
puts "Seeding #{filename}..."
load(filename) if File.exist?(filename)
end
end
end
And to use this do the following:
rake db:seed:single SEED=<seed_name_without_.seeds.rb>
This will look in the Rails.root/db/seeds folder for a file name without the .seeds.rb (it adds those for you).
Working example:
rake db:seed:single SEED=my_custom_seed
The above would seed the Rails.root/db/seeds/my_custom_seed.seeds.rb file
Too complicated!
I just wanted a simple task to execute every file under db/seeds directory without passing in any file names.
# lib/tasks/seed.rake
desc "Run all files in db/seeds directory"
namespace :db do
task seed: :environment do
Dir[File.join(Rails.root, 'db', 'seeds', '*.rb')].each do |filename|
puts "seeding - #{filename}. for reals, yo!"
load(filename)
end
end
end

How can I cleanly deploy RSpec & Cucumber HTML results with my Rails app?

I work with less technical stakeholders and testers on producing a Rails 3.1 app who benefit greatly from seeing the HTML output rendered by my Cucumber and RSpec tests.
I would like a way to bundle this output with the app when I deploy, but am struggling to find the cleanest way to do this. The repo is on Github and we deploy with Capistrano. I would strongly prefer keep this out of version control.
Is anyone else doing this? Thanks!
the cukes are a bit easier... add a rake task:
desc "runs cucumber features and outputs them to doc/cucumber as html"
task :html do
Dir.glob("features/*.feature").each do |story|
system("cucumber",
"#{story}",
"--format=html",
"-o",
"doc/cucumber/#{story.gsub(/features\/(\w*).feature/, '\1.html')}")
end
end
I don't know why the obvious solution didn't occur to me earlier: gitignore the doc files and add a deployment task to scp the files directly into the app's public directory.
.gitignore
public/doc
lib/tasks/doc.rake
namespace :doc do
desc "run features and specs then generate pages accessible in public/doc"
task :html do
# Delete old files
puts "Clearing out old html files..."
system("rm -rf public/doc/")
system("mkdir public/doc/")
# Generate the feature html files
puts "Generating Cucumber html files..."
system("mkdir public/doc/features")
Dir.glob("features/**/*.feature").each do |story|
system("bundle exec cucumber #{story} --drb --format=html -o public/doc/#{story.gsub(/(\w*).feature/, '\1.html')}")
puts "public/doc/#{story}"
end
# Generate RSpec html files
puts "Generating RSpec html files..."
system("mkdir public/doc/spec/")
Dir.glob("spec/**/*_spec.rb").each do |spec|
system("bundle exec rspec #{spec} --drb --format html -o public/doc/#{spec.gsub(/(\w*).rb/, '\1.html')}")
end
# Generate the index file
puts "Writing index file..."
system("echo \"<html><head><title>Doc</title></head><body>\" > public/doc/index.html")
# Write test report navigation
Dir.glob("public/doc/**/*.html").each do |file|
unless file == 'public/doc/index.html'
file.gsub!(/\public\/doc\//, '')
system("echo \"<p><a href='#{file}'>#{file.gsub('.html', '')}</a></p>\" >> public/doc/index.html")
end
end
# Close index.html file
system("echo \"</body></html>\" >> public/doc/index.html")
puts "Done!"
end
end
config/deploy.rb
desc "Generate Cucumber & RSpec HTML docs"
task :generate_docs, :roles => :app do
system("bundle exec rake doc:html")
end
desc "Copy Cucumber & RSpec HTML docs outside of version control with scp"
task :copy_docs, :roles => :app do
system("tar -zcvf tmp/doc.tar.gz public/doc && rm -rf public/doc")
system("scp -r -i ~/myKey.pem tmp/doc.tar.gz #{user}##{hostname}:#{deploy_to}/current/public/doc.tar.gz && rm tmp/doc.tar.gz")
run "cd #{release_path}; tar -zxvf public/doc.tar.gz && rm public/doc.tar.gz"
end
after 'deploy', 'docs:generate_docs', 'docs:copy_docs'

Resources