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.
I am trying to write a Rake task in my deploy.rb that gets passed an array of arguments. I will run it through capistrano.
Assuming a task like the following:
desc "my task"
task :mytask, [:arguments] => :environment do |t, args|
puts 'hello'
end
When I try running cap mytask['arg1 arg2 arg3'], I get the following:
the task `mytask["arg1 arg2 arg3"]' does not exist
Any ideas? I have a task defined like this that I run locally with no problems, but defining tasks like this be run using cap does not work.
NOTE: running cap mytask without the arguments part works. This is obviously not what I want.
Rake tasks are defined by a string and called via Rake::Task["name"] internally . If cap treats your array as part of the task name string it will not work.
As 'mytask' != 'mytask["arg1 arg2 arg3"]'
I want mark the current deployed directory with my release number.
I tried this approach:
Get locally the app version, store it into a variable, and on the remote host, store it in a file.
namespace :deploy do
desc "Set a release number as the app version"
task :mark_release do
release_number = `git describe`
on roles(:web) do
execute("echo #{release_number} > #{current_path}/RELEASE")
end
end
end
The problem is, when I run this via:
cap deploy:mark_release
the command look like this:
echo v9.3.0-254-g178d1f8; > /foo/bar/current/RELEASE
The semicolon is making trouble. and my RELEASE file is of course empty.
I think it is due to some escaping made by SSHKit.
Any clues ?
I managed it:
1) I took the release number from the repo directory on the machine
2) I wrote it with a stream to a file via the upload! method
namespace :deploy do
desc "Set a release number as the app version"
task :mark_release do
on roles(:web) do
within "/foo/bar/repo/" do
upload! StringIO.new(capture(:git, "describe")), "#{current_path}/RELEASE"
end
end
end
end
Here is the solution that I came up with which doesn't require uploading a local file. It goes to the repo path to execute the git command to extract the version and then redirects the output to file. The file can then be read by the Rails app. The execute requires the different parameters to be passed in separately. https://github.com/capistrano/sshkit#the-command-map has more info about the command map and why it's needed, due to the problem of escaping and whitespace.
namespace :deploy do
before :restart, :add_revision_file
task :add_revision_file do
on roles(:app) do
within repo_path do
execute(:git, :'rev-parse', :'--short', :'HEAD', ">#{release_path}/REVISION")
end
end
end
end
Use SSHKit::Command
SSHKit::Command.new("echo #{release_number} > #{current_path}/RELEASE")
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'm looking for a way to call a single Capistrano task to perform different things to different roles. Is Capistrano able to do this, or do I have write a specific task for each role?
The standard way to do this in Capistrano:
task :whatever, :roles => [:x, :y, :z] do
x_tasks
y_tasks
z_tasks
end
task :x_tasks, :roles => :x do
#...
end
task :y_tasks, :roles => :y do
#...
end
task :z_tasks, :roles => :z do
#...
end
So yes, you do need to write separate tasks, but you can call them from a parent task and they will filter appropriately.
Actually no:
% cat capfile
server 'localhost', :role2
task :task1, :roles=>:role1 do
puts 'task1'
end
task :task2 do
task1
end
% cap task2
* executing `task2'
* executing `task1'
task1
The :roles param is passed further to run command etc but does not seem to affect whether the task is actually fired.
Sorry, didn't find the way to put a comment on comment so I've written it here.
You can also do
task :foo do
run "command", :roles => :some_role
upload "source", "destination", :roles => :another_role
end
Use namespacing:
https://github.com/leehambley/capistrano-handbook/blob/master/index.markdown#namespacing-tasks
namespace :backup do
task :default do
web
db
end
task :web, :roles => :web do
puts "Backing Up Web Server"
end
task :db, :roles => :db do
puts "Backing Up DB Server"
end
end
these tasks show up in a cap -T as
backup:default
backup:web
backup:db
There is a way, kind of. Check: http://weblog.rubyonrails.org/2006/8/30/capistrano-1-1-9-beta/ and you'll see that you can override the default roles using the ROLES environment variable.
I have a task defined as:
desc "A simple test to show we can ssh into all servers"
task :echo_hello, :roles => :test do
run "echo 'hello, world!'"
end
The :test role is assigned to one server.
On the command line, I can run:
[james#fluffyninja bin]$ cap echo_hello ROLES=lots_of_servers
And the task will now run on the lots_of_servers role.
I have not verified that this works inside a ruby script by updating the ENV hash, but this is a good start.
Only for the record, this could be a solution using Capistrano 3:
desc "Do something specific for 3 different servers with 3 different roles"
task :do_something do
on roles(:api_role), in: :sequence do
# do something in api server
end
on roles(:app_role), in: :sequence do
# do something in application server
end
on roles(:another_role), in: :sequence do
# do something in another server
end
end
The sever definition to perform "do_something" task in a application server would be something like:
server 'application.your.domain', user: 'deploy', roles: %w{app_role}
Then you can call the task (there are several ways to do it) and the task will execute specific instructions according to the "app_role".