Deploying a rails app to multiple locations - ruby-on-rails

I'm trying to deploy the same rails app to two different locations with different app names, different logos, different stylesheets, etc.
I've got the code working based on an APP_NAME and a HOST_NAME variable I store in environments/production.rb. Now I need to actually deploy it, and I need a better solution than manually editing the environment file on the production machine.
The only way I can see to do it is to create a new production environment - e.g. production_app2 - and define APP_NAME and HOST_NAME differently in them. Is there a better way?

No no no! Don't edit the environment files. I mean, edit them as you need to for things that need to be configured the same for every deployment, but not for things that should be configurable between deployments.
For that, use configuration.
Throw a YAML file in config that looks something like this:
development:
:app_name: App 1
:host_name: something.com
test:
:app_name: App 1
:host_name: something.com
production:
:app_name: App 1
:host_name: something.com
Call it whatever makes sense. Let's say settings.yml.
Now load it with an initializer in config/initializers/settings.rb that looks like this:
SETTINGS = YAML.load_file("#{RAILS_ROOT}/config/settings.yml")[RAILS_ENV]
Now access your configuration like this:
SETTINGS[:app_name]
(If you don't want to change your existing code at all, inside config/initializers/settings.rb add lines that set your existing names like APP_NAME = SETTINGS[:app_name], etc.)
Note that this is one possible implementation of settings configuration, but even if another approach is taken it should be based on deployment-independent configuration. This can be much more easily and maintainably set up to persist between deployments and upgrades than mucking with environment files.
Again, to recap:
environment files are for configuration that is the same across all deployments
configuration files are for configuration that can change between deployments
Update
For Capistrano based deployments, this is what I use to symlink multiple configuration files in the new current from the shared directory (I think it originally came from an Ezra recipe from EngineYard):
after "deploy:update_code","deploy:symlink_configs"
namespace(:deploy) do
task :symlink_configs, :roles => :app, :except => {:no_symlink => true} do
configs = %w{ database settings }
configs.map! { |file| "ln -nfs #{shared_path}/config/#{file}.yml #{release_path}/config/#{file}.yml" }
run <<-CMD
cd #{release_path} && #{configs.join(' && ')}
CMD
end
end

I think that's a pretty good way.
Where we are we define different environments (e.g. 'staging', 'production', 'production_backup' - giving us a staging.rb, production.rb, production_backup.rb where you can define your specific APP_NAMEs and HOST_NAMEs) and can deploy to each of them using Capistrano. It works just fine.
This is a good link on it: http://www.egtheblog.com/?p=8

Because you are actually deploying to two different environments, it seems best to create two different environment files, each with their own settings. Make sure you pick descriptive names for your environment files, not just production2.
You could also store this information in the database, but I don't know if you're willing to accept such a dependency. I guess using a database would only make sense if the number of deployments is too large to manage easily with a few environment files.

Related

Are you using custom rails environments?

ootb rails has the 3 environments
development
test
production
How do you handle your other environments for example staging ?
When I think about what I want from a staging environment I want most of the production settings, but typically server names and other specific values are different from production.
Do you create 1:N environment/*.rb for each custom environment you have? Or do you treat the environments more as like a profile and then use something like application.yml or secrets.yml for running the application with different server configurations?
What has worked well for you in the past and what has not?
It depends if your "staging" environment is a different thing or is actually a variation on the "production" environment as is usually the case.
Normally you just deploy in full production mode, using the production.rb file and everything, to a non-production server. This distinction is irrelevant to rails, and matters only to your deployment script. For example Capistrano would be concerned about this.
The only time you need another environment is if you need a different group of settings for Rails.
In my opinion, when I run the app in an staging environment I want it to have exactly the same settings as in production, because it's when I check if everything is alright before releasing the new code. So I create a environments/staging.rb that actually is a symbolic link to environments/production.rb. To deploy, I use Capistrano, which lets you define the server settings for every environment you want to. I really encourage you to take a look at it if you don't know it, as it makes deploying as easy as running a simple command, and it takes care of uploading the new code, precompiling the assets, restarting the server, etc.
Appropriate any way described above.
In some cases I am using 1:N way because it easy when you have fixed count of deployment environments (for example when I am deploying own pet-project to remote server).
But in other cases I am using default set of environments (for example when I am deploying project to different customers).
Just choose any way appropriate for you and your project.
I use require_relative to create a staging environment that is identical to production, and then override the parts that are different. Typically the only difference is hostnames.
Here's what my environments/staging.rb looks like:
# Staging configuration is identical to production, with some overrides
# for hostname, etc.
require_relative "./production"
Rails.application.configure do
config.action_mailer.default_url_options = {
:host => "staging.example.com",
:protocol => "https"
}
config.action_mailer.asset_host = "https://staging.example.com"
end

Rails: Where to put configuration variables

My team is maintaining a Rails application. The previous developer(s) decided to put some configuration variables (like facebook app id, twitter app id) as env variables. But this is not very versatile. If we have two applications (test and production), we are forced to use only one hardcoded configuration (or changing it manually). Also it is good practice to put these variables in the version control.
Is that the right way to do that? If not, what is the right way?
We are using following solution in 2 apps.
Store config variables in yml files for https://github.com/railsjedi/rails_config gem.
Apps are configured to use different yml files in production and dev modes via initializers.
On production servers directory with yml files are symlinked with 2 apps.
Task for capistrano looks so:
namespace :settings do
task :symlink, roles: :app do
run "ln -s /path_to_settings/#{rail_env} #{current_path}/config/settings"
end
end
after "deploy:create_symlink", "settings:symlink"

Managing config in 12-factor applications

I've enjoyed using Rails on Heroku, and like that I can adjust the configuration property of a Heroku app without having to commit a change to xyz.yml and redeploy.
It would be nice to completely do away with the Yaml config files in my Rails app, and rely as much as possible on storing configuration in ENV. This goes along with the 12-factor config principle.
However, there are some trade-offs in switching from a Yaml-based configuration management to a Heroku/12-factor-based one.
While it's true that a proliferation of deployments (qa, stage, prod, dev, demo, labs) can lead to a proliferation of Yaml files, it's very easy to copy-paste to create a new configuration profile. I don't see a way to 'copy' configuration profiles from one deployment to another in Heroku.
Storing configuration data in the repo means that, in the case of Heroku, deploying and configuring and application are accomplished in a single operation. If I were to move my configuration out of Yaml files and into ENV variables, I would have to configure my application in a separate step after deployment.
Would like to hear from people who have used 12-factor style configuration in their private applications, and how they have managed lots of configuration variables across lots of deployments.
How do you quickly configure a new deployment?
Where do you keep your authoritative source of configuration variables, if not the repo? How do you distribute it among developers?
Thanks!
What I typically use is Yaml using the ENV and provide defaults. For instance, YAML can be ERB'ed happily to include your ENV vars:
foo:
var: ENV["MY_CONFIG"] || "default_value"
You just need to make sure that you load the Yaml with ERB when you read it:
YAML.load(ERB.new(File.read("#{Rails.root}/config/app_config.yml")).result)
By doing this your code works fine in dev, but also allows you to set config vars in the environment as well.
You can accomplish this relatively easy with some simple shell scripts, iterate existing variables via heroku config or heroku release:info v99, and then set heroku config:set k=v --app
But if its a problem/pain/friction perhaps you have too much inside your env var configuration.
A bit of a late answer, but I believe this is what you are looking for.
I developed a gem called settei, allowing you to use YAML files to configure the app. However the gem will serialize the YAML file as one environment variable during deploy. This way one get the best of both worlds: YAML for ease of management/creating derivative environment, and ENV for 12-factor compliance.

Using different settings locally versus remotely in files such as application.yml

In my app I have some code that changes when running locally versus in development on the remote server, such as app_domain in application.yml, etc. Besides having to manually change them each time I run locally versus deploy to remote machine, is there a better way of managing this?
EDIT - for yml
It depends a bit on how you are loading the application.yml but generally when you load yml files ruby parses it all into a hash for you. Thus you can set something up like the database.yml and structure all the yml under your different environments:
development:
domain: localhost
production:
domain: mydomain.com
test:
domain: foo
and then when you load your yml file you want to grab the settings for the specific environment you are in, like this:
MY_CONFIGS = YAML.load_file("[path to my yaml file]/application.yml")[RAILS_ENV]
I like Matthew's answer unless you have some secrets in those files (passwords).
If you have passwords you don't want in source, what I tell my UrbanDeploy customers when they have this type of scenario is to use a script (or we have a built-in script) that effectively does a token replace based on environment. Where the replacements are coming from the deployment system, or a magic file somewhere on the deployment target.
Either way, treat an environment as a first order thing that has data about it that needs to be managed.
Using kwateeSDCM you can customize any file at deployment time on a server by server basis. This way you only need one templatized application.xml and parametrize it depending on the target server.

where to put secure passwords/keys in a rails app?

I have a few web services that require secure tokens/keys/passwords to be passed in. Where should I define these secure values for my rails app to see? I want the development keys in version control, but don't want the production keys in version control. How should I set this up? I'm new to rails.
You see the question properly.
Put your passwords and keys in some yml file excluded from version control.
Then on your production server, create the very same file and symlink your app to it every time you deploy.
EDIT.
Capistrano is almost made to fits these needs:
put your yml files in the shared folder
In your capistrano deploy.rbfile:
after 'deploy' do
run "ln -s #{shared_path}/database.yml #{release_path}/config/database.yml"
end
to work with yml files: http://railscasts.com/episodes/85-yaml-configuration-file
apneadiving is right, symlinking the files is a good idea. Another approach is to put the keys in the shell variables, accessible only to the user that runs the app. Then, in your rails app you'll have
login = ENV['SERVICE_LOGIN']
password = ENV['SERVICE_PASSWORD']
As of Rails 4.1.0, check out secrets.yml.

Resources