Background
I have a rake task called prepare to update my hosts.txt file based on an environment variable I set i.e: rake spec environment=test
The mule task reads from the hosts variable in order to run the rspec tests.
Rakefile
require 'rake'
require 'rspec/core/rake_task'
hosts = IO.readlines('./hosts.txt').sort!
task :spec => 'spec:prepare'
task :spec => 'spec:mule_esb'
namespace :spec do
task :prepare do
sh ("cd ../capistrano && cap OVS_#{ENV['environment']} admin:trigger_serverspec_hosts")
end
task :mule_esb => hosts
hosts.each do |host|
begin
desc "Run serverspec on #{host}"
RSpec::Core::RakeTask.new(host) do |t|
ENV['TARGET_HOST'] = host
puts "\u2630 #{host.upcase}"
# Write to file and stdout in documentation format
t.rspec_opts = '--out rspec_results.txt --format documentation'
t.pattern = "spec/mule_esb/*_spec.rb"
t.verbose = false
# Stop serverspec from early termination if it fails on a single host
# Exit code will always be zero
t.fail_on_error = false
end
rescue
end
end
end
As you can see I have ordered the tasks as follows:
task :spec => 'spec:prepare'
task :spec => 'spec:mule_esb'
Observation
Command: rake spec environment=test
prepare task runs successfully and updates the hosts.txt file
but the mule_esb task runs from hosts set as part of the
previous run (when environment was staging)
If I run it again without any changes it successfully runs it on the test environment hosts
Running the tasks individually works as expected
rake spec spec:prepare and then rake spec:mule_esb
I'm stumped as to why this is happening. I'm not all that familiar with Rake - Can someone explain this behaviour?
Do this
namespace :spec do
task prepare: :environment do
sh ("cd ../capistrano && cap OVS_#{ENV['environment']} proteus:trigger_serverspec_hosts")
end
end
specify environment with task name.
How do I do this in the Ruby-way?
# Rakefile
desc "Run task on server #1"
task :one do
# Do somethin on server 1
Srv1.exec "..."
end
desc "Run task on server #2"
task :two do
# Do somethin on server 2
Srv2.exec "..."
end
desc "Run task on both servers"
task :both do
# Do somethin on both servers
Serv1.exec "..."
Serv2.exec "..."
end
How do I require the end execution configuration code? How do I do scaling (if I need Serv3)? Should Serv1 be a class or something else?
What I need to do is to control a Vagrant VM with Rake, run custom tasks inside Vagrant, synchronize data with the host system, run some code on production and send the result to Vagrant, etc.
At first, I'll fix a minor correction for your model. Do the both task with more simple method:
desc "Run task on both servers"
task :both => [ :one, :two ]
How do I require the end execution configuration code? How do I do scaling (if I need Serv3)? Should Serv1 be a class or something else?
Just create a class Serv, and pass to it a number of server or something else. So you'll get:
desc "Run task on a server"
task :one do
# Do somethin on a server
Serv.new( ENV[ 'SERVER_NUMBER' ] ).exec "..."
end
desc "Run task on all servers"
task :one do
# Do somethin on all servers
Serv.exec "..."
end
If you are trying to do some things on multiple servers, rake might not be quite the best tool for the job. Maybe you want to check out Capistrano which is usually used for deploying software, but also can be a tool to do general things remotely.
Ok. I've solved the problem myself with loading two files in head of Rakefile the first is server.rb with server class, the second is init.rb with server instantiating code Server1 = Server.new({:user=>'... which is accessable from tasks in Rackfile.
Are the following equivalent?
namespace :resque do
task setup: :environment do
end
end
task "resque:setup" => :environment do
end
In short: yes. When running rake resque:setup both of these tasks will be invoked.
Rake will merge these tasks. You can test this by doing the following:
p Rake.application.tasks
Which in this case would return something like
[<Rake::Task resque:setup => [environment]>]
Which is simply an Array holding a single Rake::Task object. You can also check the scope or list of namespaces for a task by doing:
p Rake.application.tasks.first.scope
#=> ["resque"]
If you want to learn a little more on how the internals of Rake work, check out Rake::Task and Rake::TaskManager
I have rake tasks which i want to run in proper sequence.
I want to run one rake task which run other rake tasks in proper sequence.
How may i do that?
you should consider defining dependencies between your tasks like this
task :primary => [:secondary]
task :secondary do
puts "Doing Secondary Task"
end
But if you really, really need to call the tasks directly you can use invoke to call another task
task :primary do
Rake::Task[:secondary].invoke
end
task :secondary do
puts "Doing Secondary Task"
end
see also here
I have a Rakefile that compiles the project in two ways, according to the global variable $build_type, which can be :debug or :release (the results go in separate directories):
task :build => [:some_other_tasks] do
end
I wish to create a task that compiles the project with both configurations in turn, something like this:
task :build_all do
[ :debug, :release ].each do |t|
$build_type = t
# call task :build with all the tasks it depends on (?)
end
end
Is there a way to call a task as if it were a method? Or how can I achieve anything similar?
If you need the task to behave as a method, how about using an actual method?
task :build => [:some_other_tasks] do
build
end
task :build_all do
[:debug, :release].each { |t| build t }
end
def build(type = :debug)
# ...
end
If you'd rather stick to rake's idioms, here are your possibilities, compiled from past answers:
This always executes the task, but it doesn't execute its dependencies:
Rake::Task["build"].execute
This one executes the dependencies, but it only executes the task if
it has not already been invoked:
Rake::Task["build"].invoke
This first resets the task's already_invoked state, allowing the task to
then be executed again, dependencies and all:
Rake::Task["build"].reenable
Rake::Task["build"].invoke
Note that dependencies already invoked are not automatically re-executed unless they are re-enabled. In Rake >= 10.3.2, you can use the following to re-enable those as well:
Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)
for example:
Rake::Task["db:migrate"].invoke
task :build_all do
[ :debug, :release ].each do |t|
$build_type = t
Rake::Task["build"].reenable
Rake::Task["build"].invoke
end
end
That should sort you out, just needed the same thing myself.
task :invoke_another_task do
# some code
Rake::Task["another:task"].invoke
end
task :build_all do
[ :debug, :release ].each do |t|
$build_type = t
Rake::Task["build"].execute
end
end
If you want each task to run regardless of any failures, you can do something like:
task :build_all do
[:debug, :release].each do |t|
ts = 0
begin
Rake::Task["build"].invoke(t)
rescue
ts = 1
next
ensure
Rake::Task["build"].reenable # If you need to reenable
end
return ts # Return exit code 1 if any failed, 0 if all success
end
end
I would suggest not to create general debug and release tasks if the project is really something that gets compiled and so results in files. You should go with file-tasks which is quite doable in your example, as you state, that your output goes into different directories.
Say your project just compiles a test.c file to out/debug/test.out and out/release/test.out with gcc you could setup your project like this:
WAYS = ['debug', 'release']
FLAGS = {}
FLAGS['debug'] = '-g'
FLAGS['release'] = '-O'
def out_dir(way)
File.join('out', way)
end
def out_file(way)
File.join(out_dir(way), 'test.out')
end
WAYS.each do |way|
desc "create output directory for #{way}"
directory out_dir(way)
desc "build in the #{way}-way"
file out_file(way) => [out_dir(way), 'test.c'] do |t|
sh "gcc #{FLAGS[way]} -c test.c -o #{t.name}"
end
end
desc 'build all ways'
task :all => WAYS.map{|way|out_file(way)}
task :default => [:all]
This setup can be used like:
rake all # (builds debug and release)
rake debug # (builds only debug)
rake release # (builds only release)
This does a little more as asked for, but shows my points:
output directories are created, as necessary.
the files are only recompiled if needed (this example is only correct for the simplest of test.c files).
you have all tasks readily at hand if you want to trigger the release build or the debug build.
this example includes a way to also define small differences between debug and release-builds.
no need to reenable a build-task that is parametrized with a global variable, because now the different builds have different tasks. the codereuse of the build-task is done by reusing the code to define the build-tasks. see how the loop does not execute the same task twice, but instead created tasks, that can later be triggered (either by the all-task or be choosing one of them on the rake commandline).