Why does Capistrano run a command differently - ruby

I have been noticing some weird stuff with Capistrano lately. But before I can explain that let me provide some head on from where it is exactly coming from.
As a part of updating my crontab on every deployment. I decided to use whenever/capistrano recipe.
But for some reason(not known to me but I see a issue in github describing the same). Every time I attempt to do so I see following error.
"database configuration does not specify adapter"
Important bit. here how the command look like
INFO [a558a04a] Running ~/.rvm/bin/rvm 2.2.2#thrasher do bundle exec whenever --update-crontab thrasher --set environment=production --roles=app,web,db as deploy#x.x.x.x
DEBUG [a558a04a] Command: cd /home/deploy/project-scp/thrasher/releases/20160129130817 && ~/.rvm/bin/rvm 2.2.2#thrasher do bundle exec whenever --update-crontab thrasher --set environment=production --roles=app,web,db
DEBUG [a558a04a] scheduling frequent upload job
scheduling daily upload job
DEBUG [a558a04a] [write] crontab file updated
INFO [a558a04a] Finished in 2.411 seconds with exit status 0 (successful).
Kindly, take a note of the command.
~/.rvm/bin/rvm 2.2.2#thrasher do bundle exec whenever ...
Now, in order to solve this problem. I set the whenever_command, a capistrano variable to look like this.
set :whenever_command,["RAILS_ENV=#{fetch(:stage)",:bundle,:exec,:whenever]
And ...
When I ran the capistrano again, I saw this.
Running /usr/bin/env RAILS=production bundle exec whenever --update-crontab thrasher --set environment=production --roles=app,web,db as deploy#x.x.x.x
DEBUG [b67e3351] Command: cd /home/deploy/project-scp/thrasher/releases/20160129131301 && /usr/bin/env RAILS=production bundle exec whenever --update-crontab thrasher --set environment=production --roles=app,web,db
Command Executed was
/usr/bin/env RAILS=production bundle exec whenever ...
Question:
Why did the 2 time the command did not look like this.
~/.rvm/bin/rvm 2.2.2#thrasher do RAILS_ENV=production bundle exec whenever
Note:
Rails 4.0.4
Capistrano Version: 3.4.0 (Rake Version: 10.5.0)
Ruby ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
Also using
capistrano/rvm
Update:
In accordance with #will_in_wi answer. To solve the whenever issue I also tried doing this.(inside deploy.rb file.)
namespace :deploy do
...
...
before "whenever:update_crontab", "deploy:set_rails_env"
end

Short answer: Placing an environment variable inside the :whenever_command is not the right way to do it. Based on the whenever/capistrano code, you should use :whenever_command_environment_variables.
Remove your :whenever_command and try this instead:
set :whenever_command_environment_variables, -> { { :rails_env => fetch(:stage) } }
Long answer: you are using capistrano/rvm, which does some magic with Capistrano's "command map" to automatically prefix certain commands with ~/.rvm/bin/rvm.... One of those certain commands is bundle, for example. If you change the whenever command to no longer start with :bundle, the mapping will no longer work.

Without seeing your Capfile, you might need to enable some subset of capistrano/rails. That is what will force RAILS_ENV to be set: https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/set_rails_env.rake

Related

Cant run Ruby script via gem "ssh-net"

I am using gem net-ssh.
The connection is established. I am able to run some simple scripts(like "pwd", "ls", etc.) successfully, but when I run the "restart" script I get an error.
Here is a snippet:
output = ssh.exec!("cd /home/csrhub/git/csrhub-api; ./bin/restart")
puts "#{output}"
And the output:
./bin/restart:7:in exec': No such file or directory - bundle (Errno::ENOENT)
from ./bin/restart:7:in'
Environment determined as "development" based on the .environment file.
Loading application.yml
And here is the script where the error occurs.
#!/usr/bin/env ruby
require 'yaml'
require_relative '../app/models/environment.rb'
config = Environment.config('application')
exec "bundle exec pumactl --control-url tcp://127.0.0.1:#{config['control_port']} --control-token #{config['control_token']} phased-restart"
Connecting via Putty, when I run the same command it gets executed without any problems.
EDIT
The PATH variable is different when I Putty:
/home/csrhub/.rbenv/shims:/home/csrhub/.rbenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
And when I run with net-ssh PATH is:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
The problem is in your ssh.exec! call. Net::SSH does not use a login shell to run your commands. A login shell is required for Bash to execute .bash_profile.
Since your rbenv setup is in .bash_profile, your exec! call will be missing your full Ruby environment, and therefore running bundle later in you restart script will fail.
The solution should be to manually execute .bash_profile, so that rbenv gets loaded:
output = ssh.exec!("source ~/.bash_profile; ...rest of commands...")
More information and other proposals here:
net-ssh and remote environment

Make shell does not inherit console shell environment variables

I am working on a Makefile to build an application where frontend and backend are completely separated in two different directories, like:
- myapp
|_be
|_fe
This is my Makefile:
.ONESHELL:
all: frontend backend
frontend:
cd ./fe && \
npm install && \
./node_modules/webpack/bin/webpack.js
backend: clean
cd ./be && \
gem install bundler && \
bundle install
clean:
rm -f ./fe/public/bundle.js && rm -rf ./fe/public/resources
webpack:
cd ./fe && \
.node_modules/webpack/bin/webpack.js
test:
cd ./fe && \
npm test
run:
cd ./be && \
rackup config.ru
The frontend is a React application and it is being built correctly when I type make frontend. No issues here.
But the backend is a Rack application written in Ruby. During the build process I need to enter its directory (be), make sure bundler is installed and run bundle install.
It happens that make creates its own shell to run the Makefile commands. And this shell does NOT inherit the environment variables of the console shell I am working. This turns out to be a problem to me, because I use rvm and rvm uses some environment variables to pick the correct gems. I use the .ruby-gemset and .ruby-version to create a private bundle to my application, not using the default gemset. This is necessary, because I have applications using many different Ruby versions and don't want to care about this.
This particular application uses JRuby 9.1.7.0 and then my Gemfile (inside be directory) has the following line:
ruby '2.3.1', :engine => 'jruby', :engine_version => '9.1.7.0'
I noticed all this issue because I got the following error when I typed make backend:
Your Ruby engine is ruby, but your Gemfile specified jruby
As far as I know rvm (and I know it quite well), this means it is trying to use my default Ruby version (which is set to Ruby 2.5.0) as engine, instead of the correct JRuby version.
Now, here is my question: Is there a way to force the make inner shell to use the same configuration of the console shell I am using?
This would solve the issue, because the console shell is already configured by rvm. I know it is because when I enter the be directory manually and run bundle install inside it, everything goes fine.
I used the .ONESHELL directive to force make to do this, but it seems it is not what I need.
Suggestions?
EDIT: make documentation says the -e flag may be used to force the make grab all console shell variables. I tried it and it didn't work.
EDIT: It all works if I do
rvm use --default jruby-9.1.7.0
before doing my make backend. But for many reasons it is not convenient to do this. Besides, I really believe this is a better way to do this. make is too smart not to allow something as simple as this.
I believe there are 2 problems:
1) when rvm creates its environment variables, it does not export them, so they are not visible to child shells, and
2) because the child shell is not started as a login shell, it does not see and process the rvm function declarations, and can therefore not execute rvm my-ruby-version.
Here are a couple of workarounds:
Before creating the child shell, do something like this:
export RVM_VERSION=`rvm current`
When starting the child shell, do so in login mode:
zsh --login
or
bash --login
Then, rvm should be a function, and this should work:
rvm $RVM_VERSION
First I added two lines to my backend: recipe, which now was reading
backend:
cd ./be && \
source $(HOME)/.rvm/scripts/rvm && \
rvm use jruby-9.1.7.0 && \
gem install bundler && \
bundle install
The idea was setting the correct Ruby (JRuby 9.1.7.0) without having to change it permanently at the console.
It happened this wouldn't work, because I wasn't choosing the shell I was using and it seems for some reason it wasn't /bin/bash and source wouldn't work. As a sequel, rvm wouldn't work either and I would receive that irritating message saying
rvm is not a function
Then I added
SHELL = /bin/bash
right after the .ONESHELL: directive.
And now everything worked fine!

bundle exec thin start vs ./bundle/bin/thin start

I'm trying to understand how bundle exec works and what it does. I've installed the gems using bundle install like so:
bundle install --binstubs ./bundle/bin --path ./bundle/lib'
This creates a script ./bundle/bin/thin that I can use to start my Rails application using thin like so:
./bundle/bin/thin start -p 8080
However I see most articles on the internet recommend starting thin using bundle exec like this:
bundle exec thin start -p 8080
What is the difference between the two? My tests show that bundle exec doesn't call the ./bundle/bin/thin script, so how does bundle exec differ from the script?
There's no significant difference: they're two ways to accomplish the same thing, which is to run the correct version of the command for your bundle, with the environment set up to ensure that other bundled gens can be loaded by the command. The choice comes down to a matter of convenience.
The benefit of bundle exec is that you don't need to generate binstubs to use it: it just works with the existing Gemfile. This explains why you don't see it invoking the binstub you do have.
Some people don't like having to type bundle exec before every command, so the goal with binstubs is that you can add the directory to the front of your PATH and call the command normally. The drawback is that there is a potential security or usability concern if a bundled gem contains a command that shadows an important system command (e.g., ls).
If you don't put it in your PATH and always call it as bundle/bin/thin, you don't have the security concern, but it also gives you no particular benefit over using bundle exec thin.
In that specific case, there is no difference. bundle exec thin start -p 8080 will end up calling .bundle/bin/thin, but what if you you installed binstubs in a different path? bundle exec thin start will read your .bundle/config to find where your binstubs folder is. If you don't have binstubs installed and say you have 3 versions of thin installed, bundle exec will execute the one that's defined in your Gemfile.
Edit: #tadman also made a good point that I initially missed. When you use bundle exec, the gem environment from your Gemfile will be used, without it, it'll load the latest version of each gem currently installed.
The bundle exec method is the "official" way to do it. If you want to make use of the --binstubs feature, which is unusual, you can.
Remember that ./bundle/bin/thin is not the script, but a wrapper or "stub" that calls the script with the proper bundle exec environment loaded.

Automatically run RSPec when plain-old Ruby (not Rails) files change

I am writing a Ruby script designed to run from the command line. The script has a corresponding RSpec file that verifies its functionality. The folder structure is:
./main_script.rb
./spec/main_script_spec.rb
Running rspec spec in the top level directory works as expected. Test results from the ./spec/main_script_spec.rb file are shown. I'd like to avoid running this manually every time I change either the main script file or the spec file. All my search results turn up things like guard which (as far as I can tell) are all designed for Rails apps.
How do I setup RSpec to watch for script or spec changes and run automatically with non-Rails Ruby code?
Like David said, Guard can be used to watch a wide variety of files and perform actions when those files are modified. It does not have to be used with a Rails app. I have set up something similar in the past using guard. Here is what I did:
Place the following in your Gemfile:
source 'https://rubygems.org'
gem 'guard'
gem 'guard-shell'
gem 'rspec'
gem 'rb-fsevent', '~> 0.9'
Then run:
$ bundle install
Create a Guardfile in your home directory with:
$ guard init
In the Guardfile, comment out the examples and add this:
guard :shell do
watch(%r{^*\.rb}) { `bundle exec rspec spec/` }
end
This tells guard to watch for modifications to any ruby files in the directory and execute the command bundle exec rspec spec/ when they change (the backticks are used to execute the command in the shell).
Then open up a new terminal window in your current directory and start a guard server to start watching the files:
$ bundle exec guard
Now your Rspec test suite should automatically run when you modify ruby files in the directory.
I used guard at the past, but now I'm using a combination of rspec focus feature and watch command.
It's very simple, just add an f before a describe of it block you want to run the test. So it would becomes fdescribe or fit block. This is the same as adding a tag :focus => true to your block.
We can then filter specs with the focus tag: rspec -t focus
Now, to keeping running theses specs (every 0.5 seconds) with focus tag we call it with watch command:
watch -n 0.5 rspec -t focus
But with that the output won't show colors. So, we need to use with unbuffer.
sudo apt-get install expect
With a little customization:
watch -n 0.5 --color 'unbuffer bundle exec rspec -t focus'
Since it's annoying to type this all, I made two alias at my ~/.bash_aliases file (your can use .bashrc as well):
alias focus="watch -n 0.5 --color 'unbuffer bundle exec rspec -t focus'"
alias focuss="bundle exec rspec -t focus"
Now I can type focus to keep running it, or for a single focus execution I type focuss
Guard can be used for plain old ruby. I generally have trouble with guard so I like to use watchr, another gem. With a few lines of code you can tell watchr to watch for changes to your files and run a command when they change.
For an example of guard with plain ruby, see the shuhari gem.
update on watchr gem: There appears to be an issue with this gem, perhaps with versions of ruby >= 2.0. The observr gem addresses this issue and works as expected in ruby 2.3.
I have used guard and the guard-rspec addition with great results, and I don't believe it to be Rails-specific. Other Ruby/RSpec projects should work equally well.
The guard documentation recommends the use of Bundler and to "always run Guard through Bundler to avoid errors". I.e. you install it through your Gemfile and always run it with bundle exec guard (or use rubygems-bundler to avoid the bundle exec part).

How to setup Whenever gem in production (RVM)

I've one rake task which i want to execute once every day:
in production installed rvm
in schedule.rb i have
set :output, "/home/username/data/public_html/log/cron_log.log"
every 24.hours do
rake "fetch:smth"
end
crontab -l shows me:
MAILTO="my.mail#gmail.com"
PATH="/usr/local/rvm/rubies/ruby-1.9.3-p125/bin/ruby "
#daily cd /home/username/data/www/nameofsite.com && RAILS_ENV=production bundle exec rake fetch:smth
truly, i'm a little bit confused, coz previously i didn't have experience with cron, so plz help.
EDIT #1
i've run rvm env -- path 1.9.3#global
and it gave me:
PATH="/usr/local/rvm/gems/ruby-1.9.3-p125/bin:/usr/local/rvm/gems/ruby-1.9.3-p125#global/bin:/usr/local/rvm/rubies/ruby-1.9.3-p125/bin:/usr/local/rvm/bin:$PATH"
and then i've
MAILTO="said.kaldybaev#gmail.com"
PATH="/usr/local/rvm/gems/ruby-1.9.3-p125/bin:/usr/local/rvm/gems/ruby-1.9.3-p125#global/bin:/usr/local/rvm/rubies/ruby-1.9.3-p125/bin:/usr/local/rvm/bin:$PATH"
#daily RAILS_ENV=production rake rate:fetch
and when i run execute, from ISPmanager, it gave me: Exited with return code = 1
the link below says that if the exit error is 1, then there is already a /var/run/cron.pid file. and it's true, but i don't have root privileges
You don't need schedule.rb if you're calling the task from cron. That's handled by the #daily entry in crontab. Just set the logfile name as an environment variable and have the rake task refer to that. You'll probably also need more in your $PATH than just the path to the ruby binary, otherwise bundle isn't going to be found. While you're giving the path to a ruby, you're not actually selecting it for RVM to know what you mean, so it's not going to be able to find the right gemset. RVM provides wrappers which Do The Right Thing for this sort of task - replace bundle exec with /usr/local/rvm/wrappers/ruby-1.9.3-p125 -S bundle exec and it should work.
Hope that gives you some ideas. There's more here.
UPDATE #1
With Edit #1, you've fixed one problem and created another. You still need to cd to the app directory, otherwise rake won't find the Rakefile.

Resources