Automate Rake Tasks

Episode #365 by Teacher's Avatar David Kimura

Summary

When rake tasks need to be part of a deployment, you could find yourself in a situation where the rake task was forgotten about or have to go through the process of shelling into the environment to execute the tasks. In this episode, we look at addressing these issues by creating a way to have rake tasks execute automatically.
rails tasks deployment 18:22

Resources

This episode is sponsored by Honeybadger

Summary

# Terminal
bin/rails g model release task
bin/rails g task release execute
bin/rails g task hello world
bin/rails g task company something

# Just a neat example of executing code from the shell
bin/rails runner "Release.all.destroy_all"

# Add in your deployment scripts
bin/rails release:execute

# lib/tasks/release.rake
require 'task_runner'

namespace :release do
  desc "Rake tasks execute each release"
  task execute: :environment do
    TaskRunner.execute("hello:world", force_run: true)
    TaskRunner.execute("company:something")
    TaskRunner.execute("company:bad")
    TaskRunner.execute("company:something")
  end
end

# lib/tasks/task_runner.rb
class TaskRunner
  def self.execute(task, force_run: false)
    new(task, force_run).execute
  end

  def initialize(task, force_run)
    @task = task
    @force_run = force_run
  end

  def execute
    puts "\n\n"
    return if already_executed?

    puts green("#{@task} execute started.")
    start_timer
    Rake::Task[@task].invoke
    stop_timer
    puts green("#{@task} finished in #{elapsed_time} seconds.")

    record_task_record
  rescue StandardError => e
    puts red("#{@task} failed. Error: #{e.message}")
  end

  private

  def already_executed?
    return false if @force_run == true

    executed = Release.find_by(task: @task).present?
    puts yellow("#{@task} already executed.") if executed
    executed
  end

  def start_timer
    @start_timer ||= Time.now.to_f
  end

  def stop_timer
    @stop_timer ||= Time.now.to_f
  end

  def elapsed_time
    '%.2f' % (stop_timer - start_timer)
  end

  def record_task_record
    return if @force_run == true
    Release.create(task: @task)
  end

  def red(string); "\e[31m#{string}\e[0m"; end
  def green(string); "\e[32m#{string}\e[0m"; end
  def yellow(string); "\e[33m#{string}\e[0m"; end
end

# lib/tasks/hello.rake
namespace :hello do
  desc "Say hello"
  task world: :environment do
    puts "Hello World!"
    sleep 0.5
  end
end

# lib/tasks/company.rake
namespace :company do
  desc "change records or something"
  task something: :environment do
    puts "this is the company something rake task"
    puts "this rake task (company:something) is being executed"
  end

  desc "bad code"
  task bad: :environment do
    puts "bad code"
    raise "this is an error"
  end
end