Rake like dependency functionality in Elixir Mix tasks? - rake

In Rake, one can specify dependencies between tasks. The engine then build a dependencies tree and perform those tasks by the order of dependencies and only once each task.
Is there a similar mechanism for that in elixir/mix ?
task seed_users: [:seed_companies] do
# actions
end
task :seed_companies do
# actions
end

I don't think there's any inbuilt functionality for this, but you can use Mix.Task.run/2 to achieve this:
defmodule Mix.Tasks.SeedUsers do
def run(_args) do
IO.puts "started seed_users"
Mix.Task.run "seed_companies"
Mix.Task.run "seed_companies"
IO.puts "completed seed_users"
end
end
defmodule Mix.Tasks.SeedCompanies do
def run(_args) do
IO.puts "started seed_companies"
IO.puts "completed seed_companies"
end
end
Example run:
$ mix seed_users
started seed_users
started seed_companies
completed seed_companies
completed seed_users
Note that Mix.Task.run/2 does not run the task if it has already been run once, so if you call Mix.Task.run/2 twice, as in the example above, it's only run once. If you'd like to run a task more than once, you need to call Mix.Task.reenable/1 after every run.

Related

Why isn't environments/test.rb required when `rake test` is run via custom Rake task?

For $REASONS, I created a Rake task to paper over rake test. However, when I use the wrapper task, config/environments/test.rb is never required and, as a result, undesirable things happen (emails are sent out, the database is dropped, etc.).
(FWIW, Rails.env and ENV['RAILS_ENV'] are still set to test in the degenerate case.)
namespace :organization do
desc "Run unit tests."
task :unit do
puts Rails.env # test
Rake::Task["test"].invoke
end
end
you need to pass the :environment in rake tasks for it to work.
namespace :organization do
desc "Run unit tests."
task unit: [:environment] do
puts Rails.env # test
Rake::Task["test"].invoke
end
end

How to make sbt run another consecutive command regardless of previous command's result?

My tests are slow. Real slow. Like I can get another cup of coffee and reading some articles while waiting for them to finished slow. So I added this task to build.sbt just to alert me when my testing is finished.
lazy val alertMe = taskKey[Unit]("Alert me when testing is completed.")
alertMe in Test := {
"say \"testing is completed\""!
}
Noted that I use say command on OS X. I then used this task like this.
;test ;alertMe
Voila! This works great.... only for successful testing. In case that any test case failed, test task return result as error, and alertMe is not invoked.
This behavior is pretty understandable. but I want my task, alert me, to run regardless of test task result. How can I do this ?
Maybe you can add test task in alertMe task, like:
lazy val alertMe = taskKey[Unit]("Alert me when testing is completed.")
alertMe := {
Command.process("test", state.value)
"say \"testing is completed\""!
}
usage: sbt alertme, it will run the test task and the shell command.
Command.process will execute the test task and without causing current task fail. so the commands always will be executed.

How to write coffee-script and sass in Rhodes / RhoMobile?

I would like to write sass and coffee-script code in Rhodes/RhoMobile and let the compiler automatically compile to js and css file before run or build task, for every platforms I am targeting
In this example you place your sass files in assets/stylesheets directory. Processed and compressed files will output in public/css. This also preserve the sub directory structure
An error during compilation will stop the process
Adjust the before hook depending on the platform your are targetting
Add this task at then very end of your Rakefile:
task :precompile_css do
Dir.chdir $app_path do
exit_code = system 'scss --force --update assets/stylesheets:public/css --style compressed'
raise 'Precompile error' unless exit_code
end
end
# Add all platforms you need.
# To get the exact name of the task to hook you can execute
# $ rake -n run:my_platform
task 'config:android' => :precompile_css
task 'config:wm' => :precompile_css
etc ...
Also possible with coffeescript
task :precompile_js do
Dir.chdir $app_path do
exit_code = system 'coffee --compile --output public/js/ assets/javascripts/'
raise 'Precompile error' unless exit_code
end
end
task 'config:android' => :precompile_js
task 'config:wm' => :precompile_js
etc...
Then just invoke your run or build tasks as usual

How do I skip a rake task

Consider the following Rake tasks:
task deploy => [:package] do
end
task package => [:build] do
end
task build do
end
Is there a way to invoke Rake on the command line to execute the package and deploy tasks, but not the build task?
Short answer, no.
The way I usually go about this is instead of using the dependant task notion like you have above:
task deploy => [:package] do
end
I create an alias task for whatever action that is to be completed:
task all => [:build, :package, :deploy]
task fastDeploy => [:package, :deploy]
task deploy do
end
task package do
end
task build do
end
It's not very elegant, but I do find it to be more readable and you can visibly see the dependency of tasks on other tasks instead of the kind of spaghetti code structure the dependant notion can result in... when you have a lot of task it can be awkward to debug the logic to figure what's gone wrong and where at times.
Hope this helps.

Capistrano how to access a serverDefinition option in the code

I am defining my server setup like this:
task :test do
role(:frontend) {[server1,server2,server3, {:user=> "frontend-user", :options => {:log_location=>"HOW DO I READ THIS??"}}]}
role(:backend) {...}
role(:db) {...}
role(:mq) {...}
end
task :staging do
role(:frontend) {[server1,server2,server3, {:user=> "frontend-user", :options => {:log_location=>"HOW DO I READ THIS??"}}]}
role(:backend) {...}
role(:db) {...}
role(:mq) {...}
end
task :prod do
role(:frontend) {[server1,server2,server3, {:user=> "frontend-user", :options => {:log_location=>"HOW DO I READ THIS??"}}]}
role(:backend) {...}
role(:db) {...}
role(:mq) {...}
end
This is to embrace all the complexity of a legacy enterpricey system.
Now, from a task, I want to read the log_location.
Task example:
namespace :log do
desc "list all log files"
task :list do
run %(ls -1 #{log_location}/*/*.log)
end
end
The problem is that the variable log_location is undefined.
/.rvm/gems/ruby-2.0.0-p0/gems/capistrano-2.14.2/lib/capistrano/configuration/namespaces.rb:193:in
method_missing': undefined local variable or methodlog_location'
for
# (NameError)
How do I access that variable?
Is there a smarter/simpler way of setting this custom variable?
I'm sorry to say you can't read that. The blocks passed to task() aren't executed in a server context, thus the block in effect doesn't know what server it's operating on.
The classical workaround for this over the years has been to upload a config file which looks something like this:
---
hostname1:
log_file_location: "/var/log/hostname1/foo/bar"
hostname2:
log_file_location: "/var/log/hostname2/foo/bar"
(or similar) and use the machines hostname when loading the configuration.
I know this isn't a great workaround, thus in the forthcoming (see the v3 branch at Github) version of Capistrano there's a feature which looks like this:
host1 = SSHKit::Host.new 'user#example.com'
host2 = SSHKit::Host.new 'user#example.org'
host1.properties = {log_file_location: "/foo/bar"}
host2.properties.log_file_location = "/bar/baz"
on hosts do |host|
target = "/var/www/sites/"
if host.hostname =~ /org/
target += "dotorg"
else
target += "dotcom"
end
execute! :head, '-n 20', host.properties.log_file_location
execute! :git, :clone, "git#git.#{host.hostname}", target
end
(SSHKit Examples) - SSHKit is the new backend driver for Capistrano.
The v3 branch probably isn't ready for prime time yet, we're having a lot of success internally but the documentation is pretty ahem non existent. However the code is quite literally an oder of magnitude less imposing, and I think you'll find quite readable.
You need this: https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension
It means that you can isolate stage specific code in separate files named after the stage. If you want to test for the stage name in the shared deploy.rb you can do that too, like this:
Put this in your deploy.rb
task :show_stage do
puts(stage)
end
Test from command line
$ cap staging show_stage
staging
Actually, I was able to pull out the log_location variable, but ended up with a solution that had one restriction:
I am using log location for one environment only. This is no problem in my current project, since I run the capistrano task against one role at a time.
For testing this setup, I made this task:
namespace :support do
desc "Test if the log location variable is correctly fetched from configuration"
task :test_log_location do
find_servers_for_task(current_task).each do |server|
# puts server.host
# puts server.port
# puts server.user
# puts server.options
result = "LOG LOCATION: #{server.options[:log_location]}"
#puts result
logger.info result
end
end
end
Then, for my tasks in the :log namespace, I defined the variable with set :log_location and also define the :current_role variable:
namespace :log do
def set_log_location
#set_log_location
#puts fetch(:log_location)
log_location = nil
options = nil
find_servers_for_task(current_task).each do |server|
# puts server.host
# puts server.port
# puts server.user
# puts server.options
options = server.options
log_location = server.options[:log_location]
#log_location = server.options[:current_role]
end
msg1="FATAL: you need to specify 'ROLES=frontend,backend,mq' (or one of them) from command line"
msg2="FATAL: Could not get log_location from environment/server options. I can only see these options: #{options}"
raise msg1 if ENV['ROLES'].nil?
raise msg2 if log_location.nil?
set :log_location, log_location
set :current_role, ENV['ROLES'].split(',').first
logger.info %(CURRENT_ROLE #{fetch(:current_role)})
logger.info %(THE LOG LOCATION IS: #{fetch(:log_location)})
end
end
Finally, I used a separate method to fully qualify the log path (needed for my setup -- also in the :log namespace):
def log_location
log_names = {
:frontend => "*/play.log",
:backend => "*Weblogic*/*.{log,out}"
}
loc = "#{fetch(:log_location)}/#{log_names[fetch(:current_role).to_sym]}"
logger.info "using the log location of '#{loc}'"
loc
end
Now, each task can use the specific log location like this:
desc "list all log files"
task :list do
set_log_location
run %(ls -l #{log_location})
end
I am sure this can be done more elegant, but it works for me