Different ENV['RAILS_RELATIVE_URL_ROOT'] per environment - ruby-on-rails

For sake of a simple example, let's say I want to deploy my dev and test environments to different subfolders. For example http://www.example.com/dev and http://www.example.com/test.
I'm using Thin, so I can easily set the relative path prefix by starting Thin with the following:
thin start --prefix /dev -e development
thin start --prefix /test -e test
However, my path helpers still generate paths that do not include the path prefix. So I end up with something like /images/logo.jpg for both environments. I'd like to end up with /dev/images/logo.jpg and /test/images/logo.jpg.
I can hard-code something like this in my config/environment.rb
ENV['RAILS_RELATIVE_URL_ROOT'] = "/dev"
But that obviously doesn't dynamically associate the relative root with the environment.

Related

Deployed digitalocean app isn't reading environment variables

I have deployed an app to digitalocean using the Ruby on Rails image. It is set up by default with a user called rails. My rails.service file looks like:
[Unit]
Description=OneMathsExamQuestions
Requires=network.target
[Service]
Type=simple
User=rails
Group=rails
WorkingDirectory=/home/rails/one_maths_exam_questions/
ExecStart=/bin/bash -lc 'bundle exec puma'
TimeoutSec=30s
RestartSec=30s
Restart=always
[Install]
WantedBy=multi-user.target
I need to use some environment variables in my application. So I have added some lines to my /home/rails/.bashrc and /root/.bashrc files (I suspect only the first one should be necessary but neither seems to work):
export A="val1"
export B="val2"
...
Now: if I call echo $A in a terminal I get the expected output. If I go into the Rails console and do ENV["A"] I get the expected output. But my app does not seem to behave correctly (the desired behaviour is connecting to Amazon S3; the exact error is not important).
If I go into my controller and explicitly log the env vars with Rails.logger.debug ENV I just get ENV, and Rails.logger.debug ENV["A"] returns empty string (I guess nil). Similarly if I try to do ENV["RAILS_ENV"] which should definitely work, I get the same. But Rails.env returns "development", as expected.
Moreover, if I explicitly write
ENV["A"] = "val1"
ENV["B"] = "val2"
...
in my config/application.rb, the app works correctly. But this is obviously not a permanent solution, since I can't commit this to version control.
I'm not using the figaro gem, which I think a lot of places are suggesting, but I don't see why I should have to since it works just fine on my local machine.
OK, it looks like if I export my environment variables in .profile then they are picked up by the server no problem. If I remove them from .bashrc then the server has no problems, but I can't get the variables in terminal. I guess they just do different things?

Rails 4, Capistrano 3 and Dotenv - How to deploy using server-side .env file

I have a Rails 4 app with Dotenv gem to read variables from the file .env.
There are some variables I've set in order to have a mysql user other than "root" for my rails app, for example:
MYSQL_ROOT_USER='rootuser'
MYSQL_ROOT_PASSWORD='rootpassword'
APP_DATABASE_USER='mydbuser'
APP_DATABASE_PASSWORD='userpassword'
I've also created a bash script to create the mysql user under scripts/database_setup.bash
#!/bin/bash
source ../.env
# creates the user
mysql -u${MYSQL_ROOT_USER} --password="${MYSQL_ROOT_PASSWORD}" -e "CREATE USER '${APP_DATABASE_USER}'#'localhost' IDENTIFIED BY '${APP_DATABASE_PASSWORD}';"
# grants permission
mysql -u${MYSQL_ROOT_USER} --password="${MYSQL_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON \`myapp\_%\`.* TO '${APP_DATABASE_USER}'#'localhost';"
On the server side, Capistrano deploys to `/home/myuser/apps/myapp/
I have three questions:
Where is the best place to put my server-side .env file? Right now I'm putting it in /home/myuser/apps/myapp/ directory.
How can I tell Capistrano to copy it to Rails root directory?
How can I tell Capistrano to execute my bash script before running migrations?
If anyone is still having troubles with this, here is how I got my .env working in production with Capistrano 3.5+:
Add your .env.production to /shared directory in production. Then, inside the deployment script, use Capistrano's append command to load linked files from the /shared directory like so:
append :linked_files, ".env.production"
Run the standard deploy (cap production deploy)
You can test whether env vars were appended by launching the rails console in production mode (rails c p) from inside the /current directory.
In production environment I think you shouldn't use .env at all.
Probably it's better to put the ENV vars in:
/etc/environment
by writing your variables like this:
export ENV_VARIABLE=value

Thin Server - Virtual Path

In production, we deploy our application to a virtual path:
https://hostname/my-virtual-path/
So the route '/users/' in development is actually accessed on https://hostname/my-virtual-path/ in production.
This means that routes are different between development (/users/) and production (/my-virtual-path/users/). Normally this is handled by setting the environment variable RAILS_RELATIVE_URL_ROOT or config.action_controller.relative_url_root so the paths Rails generates with its URL helpers is adjusted depending on the setting of these variables. Unfortunately, our assets are going to be pre-compiled BEFORE we know what this setting will be.
Is there a way to run a Rails server in development mode, using thin, that will serve files to a virtual path? For example, I want to type:
thin start --ssl -p 3000 --path whatever-i-want
and be able to access the root URL at https://localhost:3000/whatever-i-want in order to test my application.
I think you’re after the --prefix option. From the output to thin -h:
--prefix PATH Mount the app under PATH (start with /)
In your case something like this:
thin start --ssl -p 3000 --prefix /whatever-i-want
(Note you need to start the prefix with /.)

Re-source .bashrc when restarting unicorn?

I have some ENV variables that are sourced for the deploy user. (Similar to what Heroku recommends, but without using Heroku.)
My rails app depends on these for certain functions, for example, in application.rb:
config.action_mailer.default_url_options = { host: ENV['MY_HOST'] }
This is necessary because we have several staging hosts. Each host has MY_HOST defined to its correct hostname in .bashrc like so:
export MY_HOST="staging3.example.com"
This allows us to only use one rails staging environment, but still have each host's correct hostname used for testing, sending email, etc since this can be set on a per-machine basis.
Unfortunately it looks like when I restart Unicorn using USR2 it doesn't pick up changes to those variables. Doing a hard-stop and start will correctly load any changes.
I'm using preload_app = true which may I'm guessing has something to do with it. Any ideas?
In the end I went away from this approach altogether in favor of loading my app config from an app_config.yml file. Ryan Bates covers this approach in Railscast #226.
The only thing I did differently is that I load a shared app_config.yml for each server I use. Since I'm using capistrano, I just symlink the file on deploy.
For example, on staging2 my shared/configs/app_config.yml looks like this:
staging:
host: "staging2.example.com"
... whereas on staging3 it looks like this:
staging:
host: "staging3.example.com"
Now my application.rb has this line instead:
config.action_mailer.default_url_options = { host: APP_CONFIG[:host] }
I removed the actual config/app_config.yml from git so that it's not included on deploy. (I moved it to config/app_config.yml.template.) Then on deploy I use a capistrano task to symlink shared/configs/app_config.yml to config/app_config.yml:
namespace :deploy do
desc "Symlinks the app_config.yml"
task :symlink_app_config, :roles => [:web, :app, :db] do
run "ln -nfs #{deploy_to}/shared/config/app_config.yml #{release_path}/config/app_config.yml"
end
end
This strategy has these benefits over using ENV vars:
Deployed to all nodes via capistrano
We can do conditional hosts by simply changing the file on the appropriate server
Unicorn will get changes with USR2 since everything's done inside of rails
Everything's kept in one place, and the environment isn't affected by some other variables outside of the codebase

Changing the base URL for Rails 3 development

I know I'm going to deploy to an environment with my application running with a base URL which looks like this:
http://someserver/mydepartment/myapp
My development environment is set up to use the default Rails configuration, which looks like this:
http://localhost:3000/myapp
I'd like to model this deployment path in my development environment. That is, I'd like to develop with a base URL which looks like this:
http://localhost:3000/mydepartment/myapp
That way, I can make all my URLs relative to "/" and they will work in both environments.
How can I change it so my application will live at this path in my development environment?
Solutions I've found, but don't work for me:
Setting the scope in routes.rb doesn't seem to work for the static content in public.
Using Apache's rewriting capabilities. I don't want to install Apache on my development box. Ideally the solution would work with WEbrick, though I seem to have Mongrel mostly working as well (there are some problems with Mongrel and Ruby 1.9.2).
Setting relative_url_root and similar suggestions which don't work with Rails 3.
Dynamically generating CSS/JavaScript and adjusting the paths to compensate between development and production environments.
You can try mapping your rails app rack config to a different base_uri.
All you need to do is wrap the existing 'run' command in a map block
try doing this in your rails 'config.ru' file:
map '/mydepartment' do
run Myapp::Application
end
Now when you 'rails server' the app should be at localhost:3000/mydepartment .
Not sure if this will give you the desired outcome, but worth a try.
Here’s how you can deploy a Rails 3.1 app to a subdirectory in Apache, replacing config.action_controller.relative_url_root which no longer exists.
In config/routes.rb:
scope 'my_subdir' do
# all resources and routes go here
end
In your Apache configuration file:
Alias /my_subdir /var/www/my_subdir/public
<Location /my_subdir>
SetEnv RAILS_RELATIVE_URL_ROOT "/my_subdir"
PassengerAppRoot /var/www/my_subdir
</Location>
And it should work, including automatically pointing all your assets to /my_subdir.

Resources