I have a Rakefile like this
task :clean do
sh 'rm ./foo'
end
I want to prevent it from reporting error when the file 'foo' does not exist. How to do that?
I think what I want is: Is there a way to check the file first and then decide what to do next.
For example:
file 'aaa' => 'bbb' do
sh 'cp bbb aaa'
end
This task depends on the existence of file 'bbb', so I want to know can I tell Rake that my task depends on the non-existence of file 'foo' ?
You can do this by extending rake a bit:
Rakefile:
require File.join(File.dirname(__FILE__), 'unfile_rake_ext')
unfile 'target.txt' do
File.delete('target.txt')
end
unfile_rake_ext.rb:
class UnFileTask < Rake::FileTask
def needed?
File.exist?(name)
end
end
def unfile(*args, &block)
UnFileTask.define_task(*args, &block)
end
And my console output:
D:\Projects\ZPersonal\tmp>ls
Rakefile unfile_rake_ext.rb
D:\Projects\ZPersonal\tmp>touch target.txt && ls
Rakefile target.txt unfile_rake_ext.rb
D:\Projects\ZPersonal\tmp>rake target.txt --trace
** Invoke target.txt (first_time)
** Execute target.txt
D:\Projects\ZPersonal\tmp>ls
Rakefile unfile_rake_ext.rb
D:\Projects\ZPersonal\tmp>rake target.txt --trace
** Invoke target.txt (first_time, not_needed)
D:\Projects\ZPersonal\tmp>ls
Rakefile unfile_rake_ext.rb
Hope this helps.
In your rakefile:
task :clean do
rm 'foo' if File.exists? 'foo'
end
file 'aaa' => ['bbb', :clean] do |t|
cp t.prerequisites[0], t.name
end
Now at the command line:
echo 'test' > bbb
rake aaa
=> cp bbb aaa
touch foo
rake aaa
=> rm foo
=> cp bbb aaa
How about this?
if File.exists? './foo/'
sh 'rm -f ./foo'
end
Related
Whenever I call sh from rake it often echos the command that will be ran right before it is run. How can I prevent sh from logging the commands to stdout. I'd like to prevent this as I have api keys in the command I am calling, and I don't want to expose them in my build log.
There are two parts to solving this. The first is to pass the verbose: false option, which will prevent the command from being printed before it's executed:
$ cat Rakefile
SECRET = 'foobarbaz'
task :foo do
sh "echo #{SECRET} > secrets.txt", verbose: false
end
$ rake foo
(no output)
However, this doesn't help if there's an error, since Rake will print the failed command if it returns an error:
$ cat Rakefile
SECRET = 'foobarbaz'
task :foo do
sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false
end
$ rake foo
rake aborted!
Command failed with status (1): [echo foobarbaz > secrets.txt; exit 1...]
...
The solution is hinted at in the docs for sh:
If a block is given, upon command completion the block is called with an OK flag (true on a zero exit status) and a Process::Status object. Without a block a RuntimeError is raised when the command exits non-zero.
You can see where the default behavior comes from in the Rake source. The solution, then, is to supply our own block:
$ cat Rakefile
SECRET = "foobarbaz"
task :foo do
sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false do |ok, status|
unless ok
fail "Command failed with status (#{status.exitstatus}): [command hidden]"
end
end
end
$ rake foo
rake aborted!
Command failed with status (1): [command hidden]
...
Looks good!
If you find yourself needing this in multiple places, you could write a convenience method; something like this:
def quiet_sh(*cmd)
options = (Hash === cmd.last) ? cmd.pop : {}
options = { verbose: false }.merge(options)
sh *cmd, options do |ok, status|
unless ok
fail "Command failed with status (#{status.exitstatus}): [command hidden]"
end
end
end
SECRET = "foobarbaz"
task :foo do
quiet_sh "do_secret_things"
end
I have a task in my Rakefile
desc "Create a new person."
task :add_person, :name do |t, args|
sh "echo My name is #{args.name}"
end
When I do $ rake add_person john
I get:
echo My name is
My name is
rake aborted!
Don't know how to......
But I should get:
echo My name is john
My name is john
rake aborted!
Don't know how to......
What's going on?
Call it like this:
rake add_person[john]
or if you're using zsh:
rake add_person\[john\]
edit: response to comment about being able to call it like: rake add_person john
I think you have 2 options.
Use the environment variable method for passing args:
desc "Create a new person."
task :add_person do |t, args|
name = ENV.fetch('NAME')
sh "echo My name is #{name}"
end
then call like this:
$ rake add_person NAME=john
or, Hack using ARGV (not personally recommended):
desc "Create a new person."
task :add_person do
name = ARGV.last
sh "echo My name is #{name}"
# Task does nothing
task name.to_sym do ; end
end
then use like this:
$rake add_person john
personally I'd just use it as in intended though.
Update the task
task :add_person, [:name] => :environment do |_t, args|
puts "My name is #{args[:name]}"
end
and run
rake add_person['john']
I'm trying to write a Clockwork recipe for Capistrano 3. After having a look at Sidekiq's recipe I've come up with this:
namespace :load do
task :defaults do
set :clockwork_default_hooks, -> { true }
set :clockwork_pid, -> { 'tmp/pids/clockwork.pid' }
set :clockwork_log, -> { "#{current_path}/log/clockwork.log" }
set :clockwork_roles, -> { :app }
set :clockwork_config, -> { 'clock.rb' }
end
end
namespace :deploy do
before :starting, :check_clockwork_hooks do
invoke 'clockwork:add_default_hooks' if fetch(:clockwork_default_hooks)
end
end
namespace :clockwork do
def clockwork_pid_full_path
if fetch(:clockwork_pid).start_with?('/')
fetch(:clockwork_pid)
else
"#{current_path}/#{fetch(:clockwork_pid)}"
end
end
task :add_default_hooks do
after 'deploy:updated', 'clockwork:stop'
after 'deploy:reverted', 'clockwork:stop'
after 'deploy:published', 'clockwork:start'
end
desc 'Stop clockwork'
task :stop do
on roles fetch(:clockwork_roles) do
if test "[ -f #{clockwork_pid_full_path} ]"
within current_path do
execute "kill -int $(cat #{clockwork_pid_full_path}) 2>/dev/null"
end
else
execute "echo 'clockwork was not running'"
end
end
end
desc 'Start clockwork'
task :start do
on roles fetch(:clockwork_roles) do
within current_path do
with rails_env: fetch(:rails_env, 'production') do
execute "export RAILS_ENV=$RAILS_ENV"
execute :bundle, :exec, :clockwork, "#{fetch(:clockwork_config)} >> #{fetch(:clockwork_log)} 2>&1 &"
end
execute "ps -eo pid,command | grep clockwork | grep -v grep | awk '{print $1}' > #{clockwork_pid_full_path}"
end
end
end
desc 'Restart clockwork'
task :restart do
invoke 'clockwork:stop'
invoke 'clockwork:start'
end
end
However, the clockwork:start task only works if I remove the 2>&1 & part at the end. If I try to start the process in the background, nothing seems to happen.
What am I doing wrong?
This is what I ended up with. I adapted it from a Gist I found.
namespace :clockwork do
desc "Stop clockwork"
task :stop do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log stop"
end
end
end
end
desc "Clockwork status"
task :status do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log status"
end
end
end
end
desc "Start clockwork"
task :start do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log start"
end
end
end
end
desc "Restart clockwork"
task :restart do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :clockworkd, "-c clock.rb --pid-dir=#{cw_pid_dir} --log-dir=#{cw_log_dir} --log restart"
end
end
end
end
def cw_log_dir
"#{shared_path}/log"
end
def cw_pid_dir
"#{shared_path}/tmp/pids"
end
def rails_env
fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
end
end
Try this gem capistrano-clockwork.
It's the most easy way that worked very well to me. I tried several options of 'gists' but no success.
You just need update this files Gemfile, Capifile and deploy.rb.
Gemfile:
gem 'daemons'
gem 'capistrano-clockwork', group: :development
Capfile:
require 'capistrano/clockwork'
deploy.rb:
set :clockwork_file, "lib/name_of_your_clockwork_config.rb"
Remember to execute bundle install before deploy ;)
I'm a beginner in programming and wrote this little program:
Test.rb:
# encoding: utf-8
require 'open-uri'
require 'nokogiri'
def parse_file
doc = Nokogiri::XML(File.open("test.xml"))
parse_xml(doc)
end
def parse_xml(doc)
doc.root.elements.each do |node|
parse_tracks(node)
end
end
def parse_tracks(node)
if node.node_name.eql? 'kapitel'
puts 'New Kapitel'
end
end
I know how to execute this code:
ruby test.rb
But how can I call the def parse_file?
Simply add whatever you want to the end of your file. Ruby scripts are simply scripts, they are being interpreted:
…
end
parse_file # ⇐ HERE YOU GO
You can either call the method at the end of your test.rb file:
def parse_file
# ...
end
parse_file
And run it with
$ ruby test.rb
Or leave the file as it is, require it as a library and call the method manually:
$ ruby -r test.rb -e "parse_file"
Rather than hard-coding your file path, you can pass it as an argument when calling your script. Arguments can be accessed via the ARGV array:
def parse_file(file)
doc = Nokogiri::XML(File.open(file))
parse_xml(doc)
end
parse_file(ARGV.first)
Now you can run it with:
$ ruby test.rb test.xml
Another option is to make the script executable. Add a shebang as the first line of you file:
#!/usr/bin/env ruby
And set the execute flag:
$ chmod +x test.rb
Now you can run it with:
$ ./test.rb test.xml
just add
parse_file
in the end of your ruby file
In my executable Ruby file I have the following:
#!/usr/bin/env ruby
require 'thor'
include Thor::Actions
class UI < Thor
# def self.source_root
# File.dirname(__FILE__)
# end
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcalmakecal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
In the terminal when I run the file as is I get an empty line as Thor's run command is returning a NilClass.
However, when I un-comment the puts `ls ~` and comment out Thor's run method I get an output of my home directory as expected.
I'm having trouble figuring out why I can't get Thor's run method to work like Ruby's ticks.
Any ideas where I may have went wrong?
Thanks for looking
I didn't put the include statement inside my class and that messed things up. The code should be:
#!/usr/bin/env ruby
require 'makecal'
class UI < Thor
include Thor::Actions
# def self.source_root
# File.dirname(__FILE__)
# end
#
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
Thor's documentation on this method is actually wrong and incomplete. It documents that it returns the "contents of the command" (which I assume means the standard output), but it, by defualt, does nothing.
But, you can, apparently, use the :capture option to get what you want:
unless options[:pretend]
config[:capture] ? `#{command}` : system("#{command}")
end
So, try doing
puts run("ls ~", :capture => true)
And see if that does it.