Continue multi-host tests even on failure - ruby

I've built some serverspec code to run a group of tests on multiple hosts. The trouble is that the testing stops at the current host when any test fails. I want it to continue to all hosts even if a test fails.
The Rakefile:
namespace :spec do
task :all => hosts.map {|h| 'spec:' + h.split('.')[0] }
hosts.each do |host|
begin
desc "Run serverspec to #{host}"
RSpec::Core::RakeTask.new(host) do |t|
ENV['TARGET_HOST'] = host
t.pattern = "spec/cfengine3/*_spec.rb"
end
rescue
end
end
end
Complete code:
https://gist.github.com/neilhwatson/1d41c696102c01bbb87a

This behaviour is controlled by RSpec::Core::RakeTask#fail_on_error so in order to have it continue on all hosts you need to add t.fail_on_error = false. I also think that you don't need to rescue.
namespace :spec do
task :all => hosts.map {|h| 'spec:' + h.split('.')[0] }
hosts.each do |host|
desc "Run serverspec to #{host}"
RSpec::Core::RakeTask.new(host) do |t|
ENV['TARGET_HOST'] = host
t.pattern = "spec/cfengine3/*_spec.rb"
t.fail_on_error = false
end
end
end

Related

Limit scope of rule defined inside namespace

I have the following Rakefile (this is a simplified example):
namespace :green do
rule(/^build:/) do |t|
puts "[green] #{t}"
end
task :start do
puts '[green] start'
end
task run: ['build:app', :start]
end
namespace :blue do
rule(/^build:/) do |t|
puts "[blue] #{t}"
end
task :start do
puts '[blue] start'
end
task run: ['build:app', :start]
end
I would like each "build" rule to apply only within the namespace where it's defined. In other words, this is what I want to happen:
$ rake blue:run
[blue] build:app
[blue] start
But what actually happens is this (with Rake 12.3.1):
$ rake blue:run
[green] build:app
[blue] start
Is there a way to limit the scope of the "build" rules so that the rule defined in the "green" namespace isn't accessible from the "blue" namespace?
It appears that Rake doesn't support this natively. Tasks are scoped to the namespace they are defined in (by adding the scope path as a prefix), but rules get no such prefix.
I was able to get this to work by monkey-patching Rake, which is not ideal:
# Monkey-patch rake
module Rake
module TaskManager
# Copied from rake 12.3.1 and enhanced for scoped rules
def lookup_in_scope(name, scope)
loop do
tn = scope.path_with_task_name(name)
task = #tasks[tn]
return task if task
break if scope.empty?
# BEGIN ADDED LINES
task = enhance_with_matching_rule(tn)
return task if task
# END ADDED LINES
scope = scope.tail
end
nil
end
end
module DSL
# Create a rule inside a namespace scope
def scoped_rule(name, &block)
pattern = "^#{Rake.application.current_scope.path}:#{name}:"
Rake.application.create_rule(Regexp.new(pattern), &block)
end
end
end
namespace :green do
scoped_rule :build do |t|
puts t
end
task :start do |t|
puts t
end
task run: ['build:app', :start]
end
namespace :blue do
scoped_rule :build do |t|
puts t
end
task :start do |t|
puts t
end
task run: ['build:app', :start]
end
Output:
$ rake green:run
green:build:app
green:start
$ rake blue:run
blue:build:app
blue:start
I know that it's not the best solution, but I think it maybe can help you.
rule(/^build*/) do |t|
Rake::Task["green:build"].invoke if ARGV[0].start_with? "green"
Rake::Task["blue:build"].invoke if ARGV[0].start_with? "blue"
end
namespace :green do
task :build do
puts '[green] build'
end
task :start do
puts '[green] start'
end
task run: ['build.app', :start]
end
namespace :blue do
task :build do
puts '[blue] build'
end
task :start do
puts '[blue] start'
end
task run: ['build.app', :start]
end
Testing it we have:
rake green:run # [green] build
# [green] start
rake blue:run # [blue] build
# [blue] start

How do you use multiple tasks using Rake::TestTask?

I am running Minitest with Rake and would like to have two separate Rake tasks.
I have added the following:
require 'rake/testtask'
task :default => [:test]
task :quick => [:unit]
Rake::TestTask.new do |t|
puts 'within test task'
t.libs.push 'specs'
t.pattern = 'specs/*_spec.rb'
ENV['STACK'] = 'stack1'
puts "test stack #{ENV['STACK']}"
end
Rake::TestTask.new('unit') do |t|
puts 'within unit task'
t.libs.push 'specs'
t.pattern = 'specs/*_unit.rb'
ENV['STACK'] = 'stack2'
puts "test stack #{ENV['STACK']}"
end
When I run bundle exec rake quick then I get this output:
within test task
test stack stack1
within unit task
test stack stack2
I did not expect both the tasks to run. How do I create and run two separate rake tasks? As it is now, the second one always overwrites the environment variable.
Thanks
You solve this issue by using Rake::Task["task_name"].clear like this:
task :test_task do
Rake::TestTask.new do |t|
puts 'within test task'
t.libs.push 'specs'
t.pattern = 'specs/*_spec.rb'
ENV['STACK'] = 'stack1'
puts "test stack #{ENV['STACK']}"
end
end
task :unit_task do
Rake::TestTask.new('unit') do |t|
puts 'within unit task'
t.libs.push 'specs'
t.pattern = 'specs/*_unit.rb'
ENV['STACK'] = 'stack2'
puts "test stack #{ENV['STACK']}"
end
end
task :test do
Rake::Task["unit_task"].clear
Rake::Task["test_task"].invoke
end
task :unit do
Rake::Task["test_task"].clear
Rake::Task["unit_task"].invoke
end

A rake task by .rake file

I have the following Rakefile in a Ruby 1.9.3 project:
require 'rake/testtask'
require 'json'
Rake::TestTask.new do |t|
t.pattern = "spec/**/*_spec.rb"
t.verbose = true
end
task :default => :test
namespace :omglol do
namespace :file_a do
task :foo do
# do some stuff
end
end
namespace :file_b do
task :bar do
# do some stuff
end
end
end
As you can see, the first part of this file allow to run tests, just using rake command. And the second part contains some tasks.
Actually, I have a lot of tasks inside omglol:file_a and omglol:file_b namespaces. That's why I would like to move each of them inside a file, for instance tasks/omglol/file_a.rake and tasks/omglol/file_b.rake.
Is there a best way to do so? Thanks.
Yes. Simply move the logic into the appropriate files and then require them.
Example Rakefile:
require 'rake/testtask'
require 'json'
require 'lib/tasks/omglol/file_a.rake' # <= contains your subtasks
Rake::TestTask.new do |t|
t.pattern = "spec/**/*_spec.rb"
t.verbose = true
end
task :default => :test
Then in lib/tasks/omglol/file_a.rake simply define your tasks as normal:
namespace :omglol do
namespace :file_a do
task :foo do
# do some stuff
end
end
end
The pattern would be the same for file_b.rake.

Add task dependencies to Rake::TestTask

How can I declare task dependencies to a TestTask ?
In this example, 'clean_database' task should be run before integration task
Rake::TestTask.new(:integration) do |t|
t.libs << "test"
t.test_files = FileList['test/**/integration/**/test*.rb']
t.verbose = true
end
task :clean_database => [:init] do
#...
end
Rake enables redefining existing tasks, so this should be possible (add it to your existing code):
task :integration => :clean_database
You can pass in the dependency to the TestTask initializer:
Rake::TestTask.new(:integration => :clean_database) do |t|
t.libs << "test"
t.test_files = FileList['test/**/integration/**/test*.rb']
t.verbose = true
end

Is it possible to get a list of all available rake tasks in a namespace?

Is it possible from within a rake task to get a list of tasks in a namespace? A sort of programatic 'rake -T db' ?
I've found out the answer:
tasks = Rake.application.tasks
This will return an array of Rake::Task objects that can be examined. Further details at http://rake.rubyforge.org/
As you wrote, with Rake.application.tasks you get all tasks.
But inside the namespace, you can select only the tasks of the namespace (task mytest:tasklist)
And you may restrict the tasks to a namespace (task tasklist_mytest).
require 'rake'
namespace :mytest do |ns|
task :foo do |t|
puts "You called task #{t}"
end
task :bar do |t|
puts "You called task #{t}"
end
desc 'Get tasks inside actual namespace'
task :tasklist do
puts 'All tasks of "mytest":'
puts ns.tasks #ns is defined as block-argument
end
end
desc 'Get all tasks'
task :tasklist do
puts 'All tasks:'
puts Rake.application.tasks
end
desc 'Get tasks outside the namespace'
task :tasklist_mytest do
puts 'All tasks of "mytest":'
Rake.application.in_namespace(:mytest){|x|
puts x.tasks
}
end
if $0 == __FILE__
Rake.application['tasklist'].invoke() #all tasks
Rake.application['mytest:tasklist'].invoke() #tasks of mytest
Rake.application['tasklist_mytest'].invoke() #tasks of mytest
end
You can use the grep command like this
desc 'Test'
task :test do
# You can change db: by any other namespaces
result = %x[rake -T | sed -n '/db:/{/grep/!p;}' | awk '{print$2}']
result.each_line do |t|
puts t # Where t is your task name
end
end

Resources