How to manage the difference in each environment with yml without increasing the number of RAILS_ENV

In addition to the production environment and local development environment, there are generally multiple environments such as staging environment and QA environment. How to manage the different setting values for each environment (for example, database settings, URLs and IDs of linked services, etc.) is a point to worry about when operating a Rails application. In this article, I will show you how to manage the setting values for each environment with yml by using Rails :: Application.config_for prepared by Rails without increasing RAILS_ENV.

https://speakerdeck.com/spring_mt/deep-environment-parity-cdnt-2019 Assuming that there are multiple environments, please refer to this slide for the story of trying to eliminate the difference in the sense of environment and match them.

A review of Rails :: Application.config_for

module MyApp
  class Application < Rails::Application
    config.application= config_for(:application)
  end
end

If you write it like this, it will read /application.yml under the config directory and read the setting value that matches RAILS_ENV.

Rails.configuration.x.application[:hoge]

After reading, you can get the value of the key called hoge in yml by calling it as above at the place you want to use. This feature is very convenient and recommended by Rails, so I would like to actively use it.

Should I increase RAILS_ENV for each environment?

To use Rails :: Application :: config_for as is, increase RAILS_ENV and it's simple. Certainly, it is often said that config / environments / staging.rb etc. are prepared to increase environment variables. However, I don't think this method is very good. The reason is that RAILS_ENV is an environment variable for changing the behavior of the framework called Rails, so I think that using it to change the setting value of the application like this time is out of the idea of Rails. Because I think.

Since variables are different for each environment, why not use environment variables?

Since confidential information such as database passwords is often passed through environment variables, it is natural to use all setting values as environment variables as well. However, environment variables will be set on the infrastructure side, and since it is an application setting value, you may want to manage it in the application repository, so avoid managing it with environment variables.

If you can add environment variables casually and manage them in the repository nicely, this method is also good. In our project, we manage the infrastructure using CloudFormation, but CloudFormation is managed in a separate repository, and the timing of deployment is different, so we have stopped managing it with environment variables.

Then what should I do?

https://speakerdeck.com/spring_mt/deep-environment-parity-cdnt-2019?slide=47 Do it as if it were on pages 47-51 of the slides I introduced earlier. However, only the part that uses the prehook of Entrykit is tampered with. I will explain in detail for the time being. As mentioned above, I want to manage with yml for config_for, so place yml with the following directory structure. The contents of yml are omitted, but the key at the beginning of yml must be the same key as RAILS_ENV (development, test or production)

config/stage_settings
├── circleci
│   ├── application.yml
│   ├── database.yml
│   ├── secrets.yml
│   └── sentry.yml
├── local
│   ├── application.yml
│   ├── database.yml
│   ├── secrets.yml
│   └── sentry.yml
├── production
│   ├── application.yml
│   ├── database.yml
│   ├── secrets.yml
│   └── sentry.yml
└── staging
    ├── application.yml
    ├── database.yml
    ├── secrets.yml
    └── sentry.yml

Rails :: Application.config_for is a method that can read the yml file under config, so you need to move yml under the stage_settings directory nicely under config.

In the slide introduced earlier, it was realized by using the prehook of Entrykit, but if it is a process that is always performed when Rails starts, I thought that it would be easier to describe it in config / boot.rb, so I changed it. (Config / boot.rb should be the only place to put processing before starting Rails ...)

# config/boot.rb

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)

require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.

#↑ is the code automatically generated by Rails. Original processing from here

stage = ENV.fetch('APP_STAGE', 'local')
path = File.join('config', 'stage_settings', stage)
raise "#{path} is not a directory." unless File.exist?(path)

FileUtils.cp_r(Dir.glob("#{path}/*"), File.join('config'))

Describe it like this. A new environment variable called APP_STAGE has been added, which is an environment variable to represent the environment such as staging and qa environment.

Now every time you start up, all the files under stage_settings will be copied under the config directory. In order to decide which yml to use at startup, add it to .gitignore so that yml is not committed directly under the config directory.

The reason for this is that some people in the team are developing with Docker and others are not, and I don't want to increase the hassle of having to do this to set up a local development environment. I came up with this because I thought there was a good way to do it.

Summary

--RAILS_ENV does not increase even if you want to change the setting value for each environment --I want to manage the setting value for each environment with yml --I plugged the process into boot.rb and made it feel good

Recommended Posts

How to manage the difference in each environment with yml without increasing the number of RAILS_ENV
How to use git with the power of jgit in an environment without git commands
How to determine the number of parallels
How to execute with commands of normal development language in Docker development environment
How to change the maximum and maximum number of POST data in Spark
How to find the total number of pages when paging in Java
How to insert processing with any number of elements in iterative processing in Ruby
How to get the ID of a user authenticated with Firebase in Swift
How to set environment variables in the properties file of Spring boot application
[Ruby] How to find the sum of each digit
Organized how to interact with the JDK in stages
How to boot by environment with Spring Boot of Maven
[Rough explanation] How to separate the operation of the production environment and the development environment with Rails
Summary of how to use the proxy set in IE when connecting with Java
How to number (number) with html.erb
How to add elements without specifying the length of the array
How to derive the last day of the month in Java
How to change the contents of the jar file without decompressing
I want to recreate the contents of assets from scratch in the environment built with capistrano
Find the number of days in a month with Kotlin
[Beginner] Procedure to log in to the virtual environment built with Vagrant
I tried to build the environment of PlantUML Server with Docker
Output the difference between each field of two objects in Java
[Rails] How to change the page title of the browser for each page
How to get the id of PRIMAY KEY auto_incremented in MyBatis
How to install the language used in Ubuntu and how to build the environment
[Rails] How to reset the database in production environment (Capistrano version)
How to get the length of an audio file in java
How to increment the value of Map in one line in Java
How to set the indent to 2 single-byte spaces in the JAXB implementation of the JDK
How to decorate the radio button of rails6 form_with (helper) with CSS
How to access Socket directly with the TCP function of Spring Integration
How to switch the display of the header menu for each transition page
How to convert an array of Strings to an array of objects with the Stream API
[Rails] How to get the user information currently logged in with devise
How to constrain the action of the transition destination when not logged in
How to solve the local environment construction of Ruby on Rails (MAC)!
How to change the value of a variable at a breakpoint in intelliJ
How to get the absolute path of a directory running in Java
How to display the text entered in text_area in Rails with line breaks
[Swift] How to set an image in the background without using UIImageView.
Android development, how to check null in the value of JSON object
[Rails] How to apply the CSS used in the main app with Administrate
[swift5] How to change the color of TabBar or the color of item of TabBar with code
How to reflect seeds.rb in production environment
How to sort the List of SelectItem
How to get the date in java
How to use environment variables in RubyOnRails
How to build Rails 6 environment with Docker
True / false judgment based on the condition of each difference with multiple integers
How to display the amount of disk used by Docker container for each container
How to check the latest version of io.spring.platform to describe in pom.xml of Spring (STS)
[Rails] How to register multiple records in the intermediate table with many-to-many association
[Swift] How to change the order of Bar Items in Tab Bar Controller [Beginner]
How to send custom metrics and events to datadog with laravel in docker-compose environment
[Ruby] Difference between methods with and without self in the class. About class methods and instance methods.
[Rails] How to operate the helper method used in the main application with Administrate
How to build an environment of [TypeScript + Vue + Express + MySQL] with Docker ~ Vue edition ~
How to make a unique combination of data in the rails intermediate table
What to do when ‘Could not find’ in any of the sources appears in the development environment with Docker × Rails × RSpec
How to deal with the error yaml.scanner.ScannerError: while scanning for the next token that appeared in Rails environment construction with Docker