Setting up secret_key_base on deploy with Capistrano 3 - ruby-on-rails

So I'm deploying to a Ubuntu droplet hosted on DigitalOcean a Rails 4 application running on Apache and Phusion Passenger. After deployment I've been getting 500s as a result of Rails production not finding the secret_key_base token for production. However, if I run an echo $SECRET_KEY_BASE it returns the rake secret generated by my deploy.rb.
The deploy.rb task to set that up is:
namespace :deploy do
task :start do ; end
task :stop do ; end
desc "Setup ENV variables"
task :env_vars do
on "root#xxx.xxx.xxx.xx" do
execute "export SECRET_KEY_BASE=#{`bundle exec rake secret`}"
end
end
end
before "deploy", "deploy:env_vars"
However, Rails is still not picking it up. I even ssh'd into my server and in rails console checked and ENV["SECRET_KEY_BASE"] returns the correct secret token.
I thought using Capistrano's :default_env would work, but that only seems to set up environmental variables for the deploy task, but not actually on the server. Is there any easy way to solve this solution? My fallback is to just place the secret within secrets.yml since the repo is private, but I rather not do that.

There is a gem for exactly this task:
https://github.com/capistrano-plugins/capistrano-secrets-yml
Install
Add this to Gemfile:
group :development do
gem 'capistrano', '~> 3.2.1'
gem 'capistrano-secrets-yml', '~> 1.0.0'
end
And then:
$ bundle install
Setup and usage
make sure your local config/secrets.yml is not git tracked. It should be on the disk, but gitignored.
populate production secrets in local config/secrets.yml:
production:
secret_key_base: d6ced...
add to Capfile:
require 'capistrano/secrets_yml'
create secrets.yml file on the remote server by executing this task:
$ bundle exec cap production setup
You can now proceed with other deployment tasks.

You can create a file in your server called application.yml in shared/config.
Choose any one of solution from bellow
Following code in deploy.rb will automatically symlink your application.yml
set :linked_files, %w{config/application.yml}
Or
Then symlink this application.yml with your current/config/application.yml with a simple capistrano task.

Rather than exporting env variables in deploy.rb, use dotenv to load environment variables from .env[.<staging>] into ENV when rails booting.
Follow these steps:
Add this line to the top of your Gemfile:
gem 'dotenv-rails'
Put .env.production file into the linked_file in deploy/production.rb:
set :linked_files, fetch(:linked_files, []).push('.env.production')
On the remote host, add the .env.production file in shared folder which contains this line:
SECRET_KEY_BASE=<Your secret>

Give up on environment vars and just simply read a file. Do this in secrets.yml.
production:
secret_key_base: <%= File.read('/home/me/SECRET_KEY_BASE').strip %>
Then make the secret file.
$ rake secret > ~/SECRET_KEY_BASE
Before settling on this solution I tried exporting the secret from my .bash_profile, .profile, and .bashrc (top and bottom). I also tried PermitUserEnvironment and .ssh/environment. I have no idea how an environment variable gets from "the environment" into a capistrano deployment. I ran ssh example.com printenv and saw my vars. I logged in and saw my vars. But puma started with capistrano... it always has it's own environment.

Related

Environment variables in Rails, where to put for development testing before deploying

Where do I put environment variables in Rails and can I make a file for checking the environment before running?
In a MERN stack app(all javascript), I created a 3 files in a config folder:
keys.js
dev.js
prod.js
keys.js has the following code:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./prod');
} else {
module.exports = require('./dev');
}
dev.js contains all of the variables for google, mongo, stripe, etc.
'prod.js' has the following code:
module.exports = {
googleClientID: process.env.GOOGLE_CLIENT_ID,
googleClientSecret: process.env.GOOGLE_CLIENT_SECRET,
mongoURI: process.env.MONGO_URI,
cookieKey: process.env.COOKIE_KEY,
stripePublishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
sendGridKey: process.env.SENDGRID_KEY,
redirectDomain: process.env.REDIRECT_DOMAIN
};
This config folder is in the root of the app structure.
I've done this to deploy to Heroku and it works perfectly. My development env allows me to test the APIs as it would in production. It's been ignored within my repo so the dev.js is not available on Github.
My question: where to I put the such a file in a Rails application? Can I create such a file to store my env variables?
I'd like to use the same logic if possible and I can figure the code out to do so but I'm tepid about where to put the file(s).
I like using the Figaro gem to manage my ENV variables in Rails.
Setup
Add the gem to your Gemfile:
gem 'figaro'
Install the gem by running $ bundle install
Set up Figaro: $ bundle exec figaro install (this creates a file called application.yml under the config/ directory)
Add your ENV variables to the new config/application.yml file
Use
Add your ENV variables to the config/application.yml file. For example,
# config/application.yml
google_client_id: '7381a978f7dd7f9a1117'
google_client_secret: 'abdc3b896a0ffb85d373'
mongo_uri: 'https://www.example.com'
...
You can then use the ENV variables in your Rails code, wherever you need them: ENV['google_client_id']
Different ENV variables and values for different environments...
You can specify different values for your variables for different environments, say development and test. Here is an example from Figaro's README:
# config/application.yml
pusher_app_id: "2954"
pusher_key: "7381a978f7dd7f9a1117"
pusher_secret: "abdc3b896a0ffb85d373"
test:
pusher_app_id: "5112"
pusher_key: "ad69caf9a44dcac1fb28"
pusher_secret: "83ca7aa160fedaf3b350"
Pushing ENV variables to Heroku
It sounds like you're using Heroku. Figaro makes it easy to deploy all your Rails environment variables to Heroku at once: figaro heroku:set -e production
More
There is a lot more you can do with Figaro, just check out their Github Readme.

Capistrano Devise.secret_key not set

When I try to deploy with Capistrano it gets to assets:precompile and I get this message:
01:08 deploy:assets:precompile
01 ~/.rvm/bin/rvm default do bundle exec rake assets:precompile
01 rake aborted!
01 Devise.secret_key was not set. Please add the following to your Devise initializer:
01
01 config.secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
01
01 Please ensure you restarted your application after installing Devise or setting the key.
I don't want to add my secret key into devise.rb because I don't want it in version control. I am using an environment variable for secret_key_base which Devise.secret_key is supposed to fall back on. In fact, when I ssh into the server, manually navigate to the failed release and go into the rails console I find that Devise.secret_key works fine. It seems the only time it doesn't work is during the Capistrano deploy.
Edit: I can also manually run bundle exec rake assets:precompile just fine through ssh. The problem just seems to be Capistrano, but my environment variables are in /etc/environment so they should be loaded.
You need to declare the ENV variable in your capistrano production file.
Set an env variable on your computer(or the computer, or virtual machine where deploy is being called), then add the following code to your capistrano production file.
in config/deploy/production.rb
set :default_env, {
"SECRET_KEY_BASE" => ENV['PRODUCTION_SECRET_KEY'],
....
}
You should always set env variable like this when using capistrano, because sometimes depending on the way you have capistrano set up, env variables on the server will not work, you could be using a different user to deploy, or you could be using rbenv or rvm. Whatever the reason, this is the best way to set env variables when using capistrano. And you don't have to set them on the server or keep them in version control.
Reason
Capistrano use non-login, non-interactive shell modes, which doesn't load you environment variables.
When you do ssh to server, it uses login, interactive shell modes, which loads your environment variables
Solution
Use another way to set environment variables. I often use Figaro and it works with Capistrano.
To prevent sensitive information are included in Git, add config/application.yml to Capistrano's linked_files and fill correct sensitive information to <shared directory>/config/application.yml on server

Can I use the figaro gem to set env variables in my local environment?

I use the Figaro gem to set env variables for my apps hosted on Heroku.
To set the required env variables on production I use this:
$ figaro heroku:set -e production
Can I do something similar for my local environment? Right now I'm manually calling export foo=bar for each variable.
Thanks in advance!
Yes. Make sure you have it installed in your Gemfile:
gem 'figaro'
bundle install
Then run:
figaro:install
And it will generate an application.yml file.
Make sure you use add your environment variables under the respective environments:
development:
my_env_var: "stuff"
test:
my_env_var: "stuff"

deploy database.yml with Capistrano 3 in rails app

I have a Rails 4 app that connects to a second, external database. So I added the database credentials to my database.yml file and excluded it from version control in git. I need to deploy it when I push to Heroku using Capistrano.
I found some questions raised for this task, but they don't work for me and they were answered prior to the release of Capistrano 3.
I set up capistrano by adding:
group :development, :test do
gem 'capistrano-rails', '~> 1.1.1'
end
and
running bundle install then bundle exec cap install as it says in the install instructions.
I created a new folder in my app and copied the database.yml file there: myApp/shared/config/database.yml (not sure if this was necessary)
Finally, I created the following task in deply.rb:
after "deploy:update_code","deploy:config_symlink"
namespace :deploy do
task :config_symlink do
run "cp #{shared_path}/shared/config/database.yml #{release_path}/config/database.yml"
end
end
This didn't work and threw an error when pushed to Heroku:
/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.1/lib/active_record/connection_adapters/connection_specification.rb:248:in `resolve_symbol_connection': 'pg_development' database is not configured. Available: ["production"] (ActiveRecord::AdapterNotSpecified)
I tried putting this task in the production.rb file as well as adding set :linked_files, %w{config/database.yml} to the development.rb file.
Can someone help me get the database.yml file into production on Heroku?

Capistrano doesn't set RAILS_ENV for bundler

In my Gemfile I specify what branch to use on a git repository based on RAILS_ENV. However when Capistrano deploys, it runs the bundle install command - and since it's run through a shell, the proper environment (staging) isn't set. It defaults to development and gives me an error stating that there's a mismatch between Gemfile.lock and what is installed.
You are trying to install in deployment mode after changing your
Gemfile. Run bundle install elsewhere and add the updated
Gemfile.lock to version control.
You have added to the Gemfile:
* source: git#bitbucket.org:MyRepository/manager-engine.git (at develop)
You have deleted from the Gemfile:
* source: git#bitbucket.org:MyRepository/manager-engine.git (at master)
You have changed in the Gemfile:
* manager from git#bitbucket.org:MyRepository/manager-engine.git (at develop) to no specified source
Gemfile:
RAILS_ENV = ENV['RAILS_ENV'] || 'development'
gem 'manager', git: "git#bitbucket.org:MyRepository/manager-engine.git", branch: "#{ [:production, :staging].include?(RAILS_ENV.to_sym) ? :master : :develop }"
i.e., use the 'develop' branch if the rails environment is anything other than 'production' or 'staging'.
deploy/staging.rb:
set :branch, :master
set :keep_releases, 2
set :stage, :staging
set :rails_env, 'staging'
set :bundle_without, [:development, :test]
set :deploy_to, '/home/useraccount/rails_deployments/staging.www'
server 'localhost', user: 'useraccount', roles: %w{web app db}
So to be the most concise:
In regular SSH terminal, to install the repository gem under the proper environment, I have to issue RAILS_ENV=staging bundle install. Otherwise, just running bundle install installs the repository from the develop branch. Since Capistrano just runs bundle install and doesn't include the RAILS_ENV, this problem is occurring. But doesn't Capistrano set :rails_env, or is that not a real system environment variable?
I guess if there's no better way...
I ended up using a method illustrated somewhere else in SO and modified it for my needs.
Modify Gemfile to contain:
# Read environment from A) Explicit set through command, B) Existence of override file's contents or C) Default to development
RAILS_ENV = ENV['RAILS_ENV'] || `cat .rails-env`.to_s.split.first || 'development'
If .rails-env contains nothing or doesn't exist, it defaults to development. Otherwise it takes the first word as the environment. To create the file from the command line, just type echo "your-environment-name-here" > .rails-env, assuming you're in the app's root directory.
You can also create the file upon every deploy with capistrano using the command above, or just create a symlink to the file and share it between deployments:
Deploy.rb:
set :linked_files, %w{ .rails-env }
So now the environment can be forced via a file in the root of your app called .rails-env. Explicit RAILS_ENV calls such as RAILS_ENV=test bundle exec ... will still work as advertised.

Resources