Command to know Rails project root from shell script - ruby-on-rails

Is there any obvious command (be it a rails command, rake command, or other) that I can use to get the path to the root of a rails project from within a shell script?
Context:
I'm writing a shell script. I'm going to execute that script from an arbitrary directory inside a rails project, and the script needs to determine the absolute path of the rails project root.
So, for example, if the script were
#!/bin/bash
rails_root=# get the root somehow...
and given that the root directory of my project is
/home/myuser/projects/myrailsapp
then I should be able to place that script in
/home/myuser/projects/myrailsapp/scripts/myscript
and call it from elsewhere in the project
cd /home/myuser/projects/myrailsapp/app/assets
../../scripts/myscript
and the script should be able to know the path of the root directory.
I've come up with two different ways of solving this problem:
#!/bin/bash
rails_root=$(rake about | grep 'Application root' | sed 's/Application root[ ]*//')
#!/bin/bash
rails_root=$(rails c <<-EORUBY | grep ^rails_root_is | sed 's/rails_root_is//'
puts "rails_root_is#{Rails.root}"
EORUBY
)
But I feel like there should be a more obvious way to do this. I was expecting to use an existing command.

If you really need it to be in a shell, you could:
rails_root=rails runner -e development "puts Rails.root"|tail -1

Instead of a shell script you can use rake. Besides the simplicity of dealing with a high level language you also have full access to the rails environment and you don't have to deal with all the pitfalls and issues when dealing with shell scripts which should work across platforms and shells.
To generate a rake task from rails:
rails g task foo dosomething
Which would generate an a rakefile in lib/tasks/foo.rake. From the rake task you can access the Rails root directory though Rails.root.
namespace :foo do
desc "TODO"
task dosomething: :environment do
puts "Rails root is: #{ Rails.root }"
puts "Current working directory is: #{ Dir.pwd }"
# You can invoke the shell with backticks, exec or ...
puts "You are: " + `whoami`
end
end
A few good resources:
Jason Seifer: Rake Tutorial
6 Ways to Run Shell Commands in Ruby

Not as elegant as using rake, this is a recursive shell function that depends on identifying the Gemfile - kind of a hack but quite fast. There are several other ways to determine whether you have found the Rails root.
function rr () {
if test `pwd` = $HOME
then
echo not in Rails
elif test -f Gemfile && grep Rails Gemfile > /dev/null
then
pwd
else
(cd ..; rr)
fi
}

Related

How to run a cron script with gitlab-rails

I have gitlab-rails with
GitLab 15.0.4
Rails version 6.1.4.7
Ruby version ruby 2.7.5
I want to run a gitlab-rails script from Cron. The gitlab-rails runner seems to be broken as it doesn't recognize the file argument and tries to run it as a script instead, which fails. So even if the shebang line suggested in gitlab-rails runner -h worked (which it doesn't on my system because args are not separated), gitlab-rails runner complains that the file path is not a valid script.
I could include the ruby script in a shell script, pipe it to gitlab-rails, but sacrifices .rb file syntax highlighting and intelisense in editors.
I could pipe the .rb script from within a shell script into gitlab-rails. That would solve the previous, but now I have two scripts and shell is not reliable when it comes to referencing related scripts unless I give it the library path explicitly. That seems convoluted.
Someone folks have suggested workarounds. It involves running ruby as usual and then re-executing the script with rails (gitlab-rails in my case). This still fails because of the broken gitlab-rails runner which treats the file argument as a rails command...
I've expanded on the workaround to pipe the script to gitlab-rails on standard input which does work:
#!/usr/bin/env ruby
#
# List GitLab projects
#
# Run this script with sudo
if not defined?(Rails) then
# Running in plain rubby, invoke rails runner and pipe the script into it, passing args
exec("/usr/bin/gitlab-rails", "runner", "-", *ARGV, :in=>[File.expand_path(__FILE__)])
end
# Do your GitLab work here. This is just a harmless trivial example.
Project.find_each do |project|
puts "#{project.full_path}"
end
After making this script executable chown a+x I can put it in a cron job conveniently:
00 * * * * root /path/to/my/script.rb

Edit bash PATH variable for Rails application

I have application minizinc in file ~/.bashrc, and I can call it on bash. I am building a Rails application that calls minizinc from bash, but I cannot do it. After executing this:
#cmd = ` bash -c "minizinc #{path} -n 1" `
I get the following error:
bash: minizinc: command not found
How can I change the Rails application user's PATH variable from the application? Or how do I tell the Rails application where this bash application is located?
You have several options here. The one I think best suits your case and would recommend is using the command directly, instead of calling Bash to do the same as Ruby:
#cmd = `minizinc #{path} -n 1`
If you use it like this, the command is executed in a shell with an environment similar to the one where Ruby is running. Which means that the PATH variable will be the same. So if the dir containing the executable minizinc is in PATH when you start the Rails server, it should also be in the PATH variable of the shell running that command.
Now, if you really need to use Bash in the middle, I strike it as odd that the PATH variable is not the same as in Ruby (I tried it using IRB and seems to work as expected). You can check it by replacing your command with
bash -c "echo $PATH"
It should print the same value as
puts ENV['PATH']
when run in the Rails console.
If, after checking it, you see that the PATH variable of your Rails environment is incorrect, you can set it specifically for the Rails server:
PATH="<path_to_minizinc_dir>:$PATH" rails server
This sets the value of the PATH environment variable only for the command you are about to execute, in this case rails server.
Alternatively, you can surpass all this by simply using the absolute path to the executable:
#cmd = `bash -c "/full/path/to/minizinc #{path} -n 1"`
If you provide the full path to the command you want to execute, the PATH environment variable simply won't come into play, but I imagine this would be suboptimal for your case.

Run script before running tests in cocoa unit test target

I'm currently testing an iOS app that communicates with an JSON API. I need to start a sinatra server before running the tests. The server works as a mock for the real API.
Is there any way to run a one line script like this one ruby /path/to/server.rb ?
Thanks
Go to "Mange Schemes" and select you scheme, then expand "Tests" and select "Pre-actions" and add a new run script:
Select "Provide build settings from:"
I think the variable you are looking for is ${SRCROOT}
In addition to #Sebastian answer make sure to add & after your ruby command since without it sinatra will block your tests execution.
Also it's useful to take care about post-action where you need to kill the ruby process.
Following example uses bundler and rackup to start sinatra.
Example for pre-action script:
exec > /tmp/tests-pre-actions.log 2>&1
source ~/.bash_profile
SERVER_PATH="${PROJECT_DIR}"/"Server"
cd "$SERVER_PATH"
bundle exec rackup > /tmp/server.log 2>&1 &
#get the PID of the process
PID=$!
#save PID to file
echo $PID > /tmp/sinatra.pid
Example for post-action script:
exec > /tmp/tests-pre-actions.log 2>&1
source ~/.bash_profile
PID=$(</tmp/sinatra.pid)
echo "Sinatra server pid $PID"
kill -9 $PID
config.ru for rackup gem:
require './server'
trap('TERM') {Process.kill 'INT', Process.pid}
puts 'Run sinatra'
run Sinatra::Application

Capistrano 3 execute within a directory

I'm trying to write a task for Capistrano 3 that involves executing 'composer install' within the directory of the current release. It looks something like this:
namespace :composer do
desc 'Install dependencies with Composer'
task :install do
on roles(:web) do
within release_path do
execute "#{fetch(:composer_command)} install"
end
end
end
end
composer_command is set in the staging and production files - in my particular case to php /home/user/composer.phar
For some reason this command does not actually run in the current release directory, but instead runs in the parent directory (containing current, shared, releases, etc)
I delved into this a bit further and found that when I ran a single word command, like:
within release_path do
execute "pwd"
end
It works just fine, and runs the command in the current release directory. But... when I run a command with spaces, like:
within release_path do
execute "pwd && ls"
end
It runs in the parent directory, and not the directory set by the within block.
Can someone shed some light on this? Thanks!
Smells like a Cap 3 bug.
I suggest just guaranteeing you are where you want to be from the shell perspective:
execute "cd '#{release_path}'; #{fetch(:composer_command)} install"
You can retain all the niceties of within(), with(), default_env, etc, while still keeping the natural string syntax:
within release_path do
execute *%w[ pip install -r requirements.txt ]
end
A couple of tips:
1) Capistrano uses SSHKit for a lot of things, among which command execution. In order to simplify using Composer you could configure the command map (in deploy.rb or production.rb, etc), here are 2 examples:
SSHKit.config.command_map[:composer] = "#{shared_path.join('composer.phar')}"
SSHKit.config.command_map[:composer] = '/usr/bin/env composer.phar'
Next you can execute it like so:
execute :composer, :install
2) From a security perspective it's wise to disable the php setting allow_url_fopen, but unfortunately Composer needs it enabled to function. You can use this trick to leave it disabled globally:
SSHKit.config.command_map[:composer] = "/usr/bin/env php -d allow_url_fopen=On #{shared_path.join('composer.phar')}"
Check out iniscan for more security advise on php settings.
3) Composer has an option -d, --working-dir, which you can point to the directory containing the composer.json file in order to run Composer from any other directory. This should solve your problem:
execute :composer, '-d', release_path, :install
4) You may want to take a look at the capistrano-composer project :)
Actually, your use of the within function is almost correct. You have supplied it an entire string as a command, but the doc points out that this results in unreliable behaviour (which I have experienced myself).
Let the first argument to execute be a symbol instead of a string (which contains whitespace):
within release_path do
execute fetch(:composer_command).to_sym, "install"
execute :pwd
execute :ls
end
just for reference here is the Capistrano Doc explaining why within {} does not work with arguments with whitespace. I hope this helps.

RVM isnt setting environment with cron

I'm having a rough time executing script/runner with a cron and RVM. I believe the issues lie with the rvm environment not being set before the runner is executed.
currently im throwing the error
/bin/sh: 1.sql: command not found
which is more than i've gotten earlier, so i guess that's good.
I've read this thread Need to set up rvm environment prior to every cron job but im still not really getting it. Part of the problem i think is the error reporting.
this is my runner thus far.
*/1 * * * * * /bin/bash -l -c 'rvm use 1.8.7-p352#2310; cd development/app/my_app2310 && script/runner -e development "Mailer.find_customer"'
as per the above link, i tried making a rvm_cron_runner.
i created a file and placed this in it:
#!/bin/sh
source "/Users/dude/.rvm/scripts/rvm"
exec $1
then i updated my crontab to this.
*/1 * * * * * /bin/bash -l -c '/Users/dude/development/app/my_app2310/rvm_cron_runner; rvm use 1.8.7-p352#2310; cd development/app/my_app2310 && script/runner -e development "Mailer.find_customer"'
This also has made no difference. i get no error. nothing.
Can anyone see what i'm doing incorrectly?
P.S i hope my code formatting worked.
Could you try to place the code you want to run in a separate script, and then use the rvm_cron_runner ?
So place your actions in a file called /path/cron_job
rvm use 1.8.7-p352#2310
cd development/app/my_app2310 && script/runner -e development "Mailer.find_customer"
and then in your crontab write
1 2 * * * /path/rvm_cron_runner /path/cron_job
The differences:
this does not start a separate shell
use the parameter of the rvm_cron_runner
If you would use an .rvmrc file, you could even drop the rvm use ... line, I think.
You don't need to write a second cron runner (following that logic, you might as well write a third cron runner runner). Please keep things simple. All you need to do is configure your cron job to launch a bash shell, and make that bash shell load your environment.
The shebang line in your script should not refer directly to a ruby executable, but to rvm's ruby:
#!/usr/bin/env ruby
This instructs the script to load the environment and run ruby as we would on the command line with rvm loaded.
On many UNIX derived systems, crontabs can have a configuration section before the actual lines that define the jobs to be run. If this is the case, you would then specify:
SHELL=/path/to/bash
This will ensure that the cron job will be spawned from bash. Still, your environment is missing, so to instruct bash to load your environment, you will want to add to the configuration section the following:
BASH_ENV=/path/to/environment (typically .bash_profile or .bashrc)
HOME is automatically derived from the /etc/passwd line of the crontab owner, but you can override it.
HOME=/path/to/home
After this, a cron job might look like this:
15 14 1 * * $HOME/rvm_script.rb
What if your crontab doesn't support the configuration section. Well, you will have to give all the environment directives in one line, with the job itself. For example,
15 14 1 * * export BASH_ENV=/path/to/environment && /full/path/to/bash -c '/full/path/to/rvm_script.rb'
Full blog post on the subject
You can use rvm wrappers:
/home/deploy/.rvm/wrappers/ruby-2.2.4/ruby
Source: https://rvm.io/deployment/cron#direct

Resources