In my ruby script,I required the gmail gem:
require 'rubygems'
require 'gmail'
when running in shell,it works ok:
ruby my-script.rb
while when I put it in a cron job,it failed to execuate:
* * * * * cd /to/script/directory;/usr/local/rvm/rubies/ree-1.8.7-2011.03/bin/ruby ./my-script.rb
the log shows that the gmail gem can not be loaded:
/usr/local/rvm/rubies/ree-1.8.7-2011.03/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require': no such file to load -- gmail (LoadError)
well, when i do this(put the cmd in bash -l -c 'CMD'):
* * * * * /bin/bash -l -c 'cd /to/script/directory;/usr/local/rvm/rubies/ree-1.8.7-2011.03/bin/ruby ./my-script.rb'
it works ok again.
why?
ps.I know the arg -l make the bash a login shell,but does that make any difference?
The -l parameter executes the command in a login shell, which means that it inherits your path and other settings from your shell profile. The cron job, if run without the login shell, will be run without any path environment variables set (such as those set by RVM), which results in the system being unable to find the referenced gems.
rvm requires you use a shell login, see FAQ.
I suspect you are loading RVM in your login script, ergo RVM will only be available in a login shell.
Related
I have a simple ruby script, hello.rb:
#!/usr/bin/env ruby
puts 'hello'
It runs ok at the command line:
# /usr/local/src/hello/hello.rb
hello
However, if I put it in cron:
* * * * * /usr/local/src/hello/hello.rb >> /usr/local/src/hello/hello.log 2>&1
There are errors in the log file:
/usr/bin/env: ruby: No such file or directory
/usr/bin/env: ruby: No such file or directory
...
/usr/bin/env: ruby: No such file or directory
/usr/bin/env ruby runs ok at command line though:
# /usr/bin/env ruby -v
ruby 1.8.7 (2012-10-12 patchlevel 371) [i686-linux]
How to fix the env error for cron?
The problem is that the environment isn't what you expect.
You don't say whether the cron is running as your user, or as root, but, in either case, you can test to see what the environment looks like by adding another cron entry of:
* * * * * /usr/bin/env > /path/to/your/home/directory/env.txt
Let that run once, then pull it out, and look at the file.
Instead of using /usr/bin/env to try to find a Ruby to run your code, define the Ruby explicitly:
* * * * * /path/to/the/ruby/you/want /usr/local/src/hello/hello.rb >> /usr/local/src/hello/hello.log 2>&1
You can figure out which Ruby you want by using:
which ruby
Alternately, instead of relying on /usr/bin/env in your #! line, define your Ruby there.
Using /usr/bin/env ruby in your code is a convenience when you're using something like RVM or rbenv, and switching between versions of Ruby. It's not a good choice when you're putting something into "production", whether it's on your machine in your own account, or on a production host running as root.
If you are on Linux or Mac OS, try man 5 crontab for more information. Also, "Where can I set environment variables that crontab will use?" should be very useful.
env searches only in the existing PATH variable. crond creates the process that is run as your user name. So the PATH is minimal. You have to set up your environment variables in the script itself
I have a ruby script that connects to an Amazon S3 bucket and downloads the latest production backup. I have tested the script (which is very simple) and it works fine.
However, when I schedule this script to be run as a cron job it seems to fail when it loads the Amazon (aws-s3) gem.
The first few lines of my script looks like this:
#!/usr/bin/env ruby
require 'aws/s3'
As I said, when I run this script manually, it works fine. When I run it via a scheduled cron job, it fails when it tries to load the gem:
`require': no such file to load -- aws/s3 (LoadError)
The crontab for this script looks like this:
0 3 * * * ~/Downloader/download.rb > ~/Downloader/output.log 2>&1
I originally thought it might be because cron is running as a different user, but when I do a 'whoami' at the start of my ruby script it tells me it's running as the same user I always use.
I have also done a bundle init and added the gem to my gemfile, but this doesn't seem to have any affect.
Why does cron fail to load the gem? I am running Ubuntu.
As mentioned here https://coderwall.com/p/vhv8aw you can simply try
rvm cron setup # let RMV do your cron settings
Make sure that you make copy of your crontab before running this command
If you're running it manually and it works you're probably in a different shell environment than cron is executing in. Since you mention you're on Ubuntu, the cron jobs probably execute under /bin/sh, and you're manually running them under /bin/bash if you haven't changed anything.
You can debug your environment problems or you can change the shell that your job runs under.
To debug, There are several ways to figure out what shell your cron jobs are using. It can be defined in
/etc/crontab
or you can make a cron job to dump your shell and environment information, as has been mentioned in this SO answer: How to simulate the environment cron executes a script with?
To switch to that shell and see the actual errors causing your job to fail, do
sudo su
env -i <path to shell> (e.g. /bin/sh)
Then running your script you should see what the errors are and be able to fix them (rubygems?).
Option 2 is to switch shells. You can always try something like:
0 3 * * * /bin/bash -c '~/Downloader/download.rb > ~/Downloader/output.log 2>&1'
To force your job into bash. That might also clear things up.
You may also explicitly set your Gem path:
GEM_HOME="/usr/local/rvm/gems/ruby-1.9.2-p290#my-special-gemset"
in a non cron environment execute echo $PATH, copy the path and paste it into your crontab, before your command:
echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin
and inside crontab:
PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin
0 3 * * * ~/Downloader/download.rb > ~/Downloader/output.log 2>&1
Add this at the beginning of your cron
PATH="/home/user/.rvm/gems/ruby-2.1.4/bin:/home/user/.rvm/gems/ruby-2.1.4#global/bin:/home/user/.rvm/rubies/ruby-2.1.4/bin:/home/user/.rvm/gems/ruby-2.1.4/bin:/home/user/.rvm/gems/ruby-2.1.4#global/bin:/home/user/.rvm/rubies/ruby-2.1.4/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin:/home/user/.rvm/bin:/usr/local/sbin:/usr/sbin:/home/user/.rvm/bin:/home/user/.local/bin:/home/user/bin"
GEM_HOME='/home/user/.rvm/gems/ruby-2.1.4'
GEM_PATH='/home/user/.rvm/gems/ruby-2.1.4:/home/user/.rvm/gems/ruby-2.1.4#global'
MY_RUBY_HOME='/home/user/.rvm/rubies/ruby-2.1.4'
IRBRC='/home/user/.rvm/rubies/ruby-2.1.4/.irbrc'
RUBY_VERSION='ruby-2.1.4'
I've tried all the solution above, none of them worked until I tried;
0 12 * * * /bin/bash -l -c 'ruby /Users/simon/Desktop/script.rb'
I currently have this shell script ...
nightly.sh
#!/bin/bash
rvm 1.9.2
cd /home/appname/capistrano/current
RAILS_ENV=production bundle exec rake nightly >> /home/appname/capistrano/shared/log/nightly.log 2>&1
I use it in my crontab entry here... crontab -e
42 20 * * * /home/appname/nightly.sh
When it runs I get this error
/home/appname/nightly.sh: line 4: bundle: command not found
I am using RVM
I've now Added some environmental variables to my crontab per #KL-7
SHELL=/bin/bash
HOME=/home/appname
PATH=/home/appname/local/bin:/home/appname/.rvm/gems/ruby-1.9.2-p290/bin:/home/appname/.rvm/gems/ruby-1.9.2-p290#global/bin:/home/appname/.rvm/rubies/ruby-1.9.2-p290/bin:/home/appname/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
Now I'm getting this...
/home/appname/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:247:in `to_specs': Could not find bundler
[minitest-1.6.0, rake-0.8.7, rdoc-2.5.8] (Gem::LoadError)
from /home/appname/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:256:in `to_spec'
from /home/appname/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems.rb:1210:in `gem'
from /home/appname/.rvm/gems/ruby-1.9.2-p290/bin/bundle:18:in `<main>'
It could be because its throwing an error and you are not capturing it. Try the following:
01 04 * * * /bin/bash -l -c 'cd /home/appname/capistrano/current && RAILS_ENV=production bundle exec rake nightly' >> /home/appname/capistrano/shared/log/nightly.log 2>&1
Seems like cron can't locate your bundle executable. You need to find it (using, for example, which bundle) and then either specify full path to it in crontab or set PATH environment variable at the top of crontab like that:
PATH=/bin:/usr/bin:/path/to/directory/with/bundle/
This might help:
/bin/bash -l -c
Read:
http://blog.scoutapp.com/articles/2010/09/07/rvm-and-cron-in-production
Here is another solution:
* * * * * ssh localhost 'your command here...'
This will just ssh in to the same host (which sources the normal environment) and issues the command
My ruby is in /usr/local/bin. whenever can't find it, and setting PATH at the top of my cron file doesn't work either, I think because whenever is running the command inside of a new bash instance.
# this does not work
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin
# Begin Whenever generated tasks for: foo
0 * * * * /bin/bash -l -c 'cd /srv/foo/releases/20110429110637 && script/rails runner -e production '\''ActiveRecord::SessionStore::Session.destroy_recent(15)'\'''
# End Whenever generated tasks for: foo
How can I tell whenever where my ruby binary is? Making a symbolic link from /usr/bin seems messy to me, but I guess that might be the only option.
This question offers env :PATH, "..." in schedule.rb as a solution, but (a) I can't find any documentation of that feature anywhere in the docs (b) it doesn't seem to have solved the asker's problem (unfortunately it takes non-trivial turnaround time for me to just try it).
update actually it is in the bottom of this page, i'll try it now.
more info
I can't modify the cron command because it's generated by whenever
i verified that if I make a new bash shell with bash -l, /usr/bin/env finds ruby just fine
I just tried the exact command in cron, starting with /bin/bash, from the command line of that user, and it worked.
so, this is very mysterious...
The solution is to put this in schedule.rb:
env :PATH, ENV['PATH']
Here's a little guide I put together on the topic.
rewrite your crontab as
0 * * * * { PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin ; export PATH ;/bin/bash -l -c 'cd /srv/foo/releases/20110429110637 && script/rails runner -e production '\''ActiveRecord::SessionStore::Session.destroy_recent(15)'\''' ; }
Or you should try to figure out why your BASH shell is not picking the PATH=... that is almost certainly in your .profile or .bash_profile.
I hope this helps.
As John Bachir pointed out, you can do it via env. But let me add more input. I am deploying on AWS Opsworks. Unfortunately they do not have a ruby manager (RVM, Rbenv, etc) installed by default.
The first thing I needed to do was SSH into the instance and figure out which ruby I was using. This was easy enough by executing the which ruby command in a terminal.
$ which ruby
/usr/local/bin/ruby
Cron was using ruby located at /usr/bin/ruby. This needed to be changed.
In schedule.rb, I have:
set :env_path, ''
env :PATH, #env_path if #env_path.present?
In local, env_path doesn't need to be set. For most users, the only thing to do is execute whenever as such:
bundle exec whenever --set 'environment=development' --update-crontab
On a staging / production environment, ruby may be installed elsewhere. So running this may be more appropriate:
bundle exec whenever --set 'environment=staging&env_path=/usr/bin/local' --update-crontab
You will need to replace /usr/bin/local with the output of echo $PATH.
In Opsworks, however, I needed to create a custom Chef recipe that looked like:
node[:deploy].each do |application, deploy|
execute 'whenever' do
user 'deploy'
group 'nginx'
cwd "#{deploy[:deploy_to]}/current"
command "bundle exec whenever --set 'environment=#{deploy[:environment_variables][:RAILS_ENV]}&env_path=#{ENV['PATH']}' --update-crontab"
end
end
I hope the information here is clear enough.
crontab -l gives me this
0,2,4,6,8,10 * * * * /bin/bash -l -c 'cd /home/ruben/Monitoring ; script/rails runner Ping.check_pings'
Why does this not work?
If i try "cd /home/ruben/Monitoring ; script/rails runner Ping.check_pings" in the command line it works. I have also tried it with "&&" as ";"
The problem may be related to PATH, or to some other environment variable (like GEM_HOME), that is defined properly in your command-line environment, but not in cron's environment.
crontab doesn't run with the enviroment of the user, rather it creates it's own slimmed down enviroment. This includes very small PATH - /usr/bin:/usr/sbin:. and some other variables. See more here - http://adminschoice.com/crontab-quick-reference
Easiest solution is to add '. ~/.profile' before you run rails, or to fix path in some other way.
BTW, before you try to add PATH=/my/path/here;$PATH into crontab - that syntax (variable expansion) is not allowed either