crazy-yard 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +438 -0
  4. data/bin/ey +9 -0
  5. data/lib/engineyard.rb +9 -0
  6. data/lib/engineyard/cli.rb +816 -0
  7. data/lib/engineyard/cli/api.rb +98 -0
  8. data/lib/engineyard/cli/recipes.rb +129 -0
  9. data/lib/engineyard/cli/ui.rb +275 -0
  10. data/lib/engineyard/cli/web.rb +85 -0
  11. data/lib/engineyard/config.rb +158 -0
  12. data/lib/engineyard/deploy_config.rb +65 -0
  13. data/lib/engineyard/deploy_config/ref.rb +56 -0
  14. data/lib/engineyard/error.rb +82 -0
  15. data/lib/engineyard/eyrc.rb +59 -0
  16. data/lib/engineyard/repo.rb +105 -0
  17. data/lib/engineyard/serverside_runner.rb +159 -0
  18. data/lib/engineyard/templates.rb +6 -0
  19. data/lib/engineyard/templates/ey.yml.erb +196 -0
  20. data/lib/engineyard/templates/ey_yml.rb +119 -0
  21. data/lib/engineyard/thor.rb +215 -0
  22. data/lib/engineyard/version.rb +4 -0
  23. data/lib/vendor/thor/Gemfile +15 -0
  24. data/lib/vendor/thor/LICENSE.md +20 -0
  25. data/lib/vendor/thor/README.md +35 -0
  26. data/lib/vendor/thor/lib/thor.rb +473 -0
  27. data/lib/vendor/thor/lib/thor/actions.rb +318 -0
  28. data/lib/vendor/thor/lib/thor/actions/create_file.rb +105 -0
  29. data/lib/vendor/thor/lib/thor/actions/create_link.rb +60 -0
  30. data/lib/vendor/thor/lib/thor/actions/directory.rb +119 -0
  31. data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +137 -0
  32. data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +314 -0
  33. data/lib/vendor/thor/lib/thor/actions/inject_into_file.rb +109 -0
  34. data/lib/vendor/thor/lib/thor/base.rb +652 -0
  35. data/lib/vendor/thor/lib/thor/command.rb +136 -0
  36. data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  37. data/lib/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  38. data/lib/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  39. data/lib/vendor/thor/lib/thor/error.rb +28 -0
  40. data/lib/vendor/thor/lib/thor/group.rb +282 -0
  41. data/lib/vendor/thor/lib/thor/invocation.rb +172 -0
  42. data/lib/vendor/thor/lib/thor/parser.rb +4 -0
  43. data/lib/vendor/thor/lib/thor/parser/argument.rb +74 -0
  44. data/lib/vendor/thor/lib/thor/parser/arguments.rb +171 -0
  45. data/lib/vendor/thor/lib/thor/parser/option.rb +121 -0
  46. data/lib/vendor/thor/lib/thor/parser/options.rb +218 -0
  47. data/lib/vendor/thor/lib/thor/rake_compat.rb +72 -0
  48. data/lib/vendor/thor/lib/thor/runner.rb +322 -0
  49. data/lib/vendor/thor/lib/thor/shell.rb +88 -0
  50. data/lib/vendor/thor/lib/thor/shell/basic.rb +393 -0
  51. data/lib/vendor/thor/lib/thor/shell/color.rb +148 -0
  52. data/lib/vendor/thor/lib/thor/shell/html.rb +127 -0
  53. data/lib/vendor/thor/lib/thor/util.rb +270 -0
  54. data/lib/vendor/thor/lib/thor/version.rb +3 -0
  55. data/lib/vendor/thor/thor.gemspec +24 -0
  56. data/spec/engineyard/cli/api_spec.rb +50 -0
  57. data/spec/engineyard/cli_spec.rb +28 -0
  58. data/spec/engineyard/config_spec.rb +61 -0
  59. data/spec/engineyard/deploy_config_spec.rb +194 -0
  60. data/spec/engineyard/eyrc_spec.rb +76 -0
  61. data/spec/engineyard/repo_spec.rb +83 -0
  62. data/spec/engineyard_spec.rb +7 -0
  63. data/spec/ey/console_spec.rb +57 -0
  64. data/spec/ey/deploy_spec.rb +435 -0
  65. data/spec/ey/ey_spec.rb +23 -0
  66. data/spec/ey/init_spec.rb +123 -0
  67. data/spec/ey/list_environments_spec.rb +120 -0
  68. data/spec/ey/login_spec.rb +33 -0
  69. data/spec/ey/logout_spec.rb +24 -0
  70. data/spec/ey/logs_spec.rb +36 -0
  71. data/spec/ey/rebuild_spec.rb +18 -0
  72. data/spec/ey/recipes/apply_spec.rb +29 -0
  73. data/spec/ey/recipes/download_spec.rb +43 -0
  74. data/spec/ey/recipes/upload_spec.rb +99 -0
  75. data/spec/ey/rollback_spec.rb +73 -0
  76. data/spec/ey/scp_spec.rb +176 -0
  77. data/spec/ey/servers_spec.rb +209 -0
  78. data/spec/ey/ssh_spec.rb +273 -0
  79. data/spec/ey/status_spec.rb +45 -0
  80. data/spec/ey/timeout_deploy_spec.rb +18 -0
  81. data/spec/ey/web/disable_spec.rb +21 -0
  82. data/spec/ey/web/enable_spec.rb +26 -0
  83. data/spec/ey/web/restart_spec.rb +21 -0
  84. data/spec/ey/whoami_spec.rb +30 -0
  85. data/spec/spec_helper.rb +84 -0
  86. data/spec/support/bundled_ey +7 -0
  87. data/spec/support/fixture_recipes.tgz +0 -0
  88. data/spec/support/git_repos.rb +115 -0
  89. data/spec/support/helpers.rb +330 -0
  90. data/spec/support/matchers.rb +16 -0
  91. data/spec/support/ruby_ext.rb +13 -0
  92. data/spec/support/shared_behavior.rb +278 -0
  93. metadata +411 -0
@@ -0,0 +1,59 @@
1
+ module EY
2
+ class EYRC
3
+ attr_reader :path
4
+
5
+ DEFAULT_PATH = "~/.eyrc"
6
+
7
+ def self.load
8
+ new(ENV['EYRC'] || DEFAULT_PATH)
9
+ end
10
+
11
+ def initialize(path)
12
+ @path = Pathname.new(path).expand_path
13
+ end
14
+
15
+ def exist?
16
+ path.exist?
17
+ end
18
+
19
+ def delete_api_token
20
+ delete('api_token')
21
+ end
22
+
23
+ def api_token
24
+ self['api_token']
25
+ end
26
+
27
+ def api_token=(token)
28
+ self['api_token'] = token
29
+ end
30
+
31
+ private
32
+
33
+ def [](key)
34
+ read_data[key.to_s]
35
+ end
36
+
37
+ def []=(key,val)
38
+ new_data = read_data.merge(key.to_s => val)
39
+ write_data new_data
40
+ val
41
+ end
42
+
43
+ def delete(key)
44
+ data = read_data.dup
45
+ res = data.delete(key)
46
+ write_data data
47
+ res
48
+ end
49
+
50
+ def read_data
51
+ exist? && YAML.load(path.read) || {}
52
+ end
53
+
54
+ def write_data(new_data)
55
+ path.open("w") {|f| YAML.dump(new_data, f) }
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,105 @@
1
+ require 'engineyard/error'
2
+ require 'pathname'
3
+
4
+ module EY
5
+ class Repo
6
+ class NotAGitRepository < EY::Error
7
+ attr_reader :dir
8
+ def initialize(output)
9
+ @dir = File.expand_path(ENV['GIT_DIR'] || ENV['GIT_WORK_TREE'] || '.')
10
+ super("#{output} (#{@dir})")
11
+ end
12
+ end
13
+
14
+ class NoRemotesError < EY::Error
15
+ def initialize(path)
16
+ super "fatal: No git remotes found in #{path}"
17
+ end
18
+ end
19
+
20
+ def self.exist?
21
+ `git rev-parse --git-dir 2>&1`
22
+ $?.success?
23
+ end
24
+
25
+ attr_reader :root
26
+
27
+ # $GIT_DIR is what git uses to override the location of the .git dir.
28
+ # $GIT_WORK_TREE is the working tree for git, which we'll use after $GIT_DIR.
29
+ #
30
+ # We use this to specify which repo we should look at, since it would also
31
+ # specify where any git commands are directed, thus fooling commands we
32
+ # run anyway.
33
+ def initialize
34
+ end
35
+
36
+ def root
37
+ @root ||= begin
38
+ out = `git rev-parse --show-toplevel 2>&1`.strip
39
+
40
+ if $?.success? && !out.empty?
41
+ Pathname.new(out)
42
+ else
43
+ raise EY::Repo::NotAGitRepository.new(out)
44
+ end
45
+ end
46
+ end
47
+
48
+ def ensure_repository!
49
+ root
50
+ end
51
+
52
+ def has_committed_file?(file)
53
+ ensure_repository!
54
+ `git ls-files --full-name #{file}`.strip == file && $?.success?
55
+ end
56
+
57
+ def has_file?(file)
58
+ ensure_repository!
59
+ has_committed_file?(file) || root.join(file).exist?
60
+ end
61
+
62
+ # Read the committed version at HEAD (or ref) of a file using the git working tree relative filename.
63
+ # If the file is not committed, but does exist, a warning will be displayed
64
+ # and the file will be read anyway.
65
+ # If the file does not exist, returns nil.
66
+ #
67
+ # Example:
68
+ #
69
+ # read_file('config/ey.yml') # will read $GIT_WORK_TREE/config/ey.yml
70
+ #
71
+ def read_file(file, ref = 'HEAD')
72
+ ensure_repository!
73
+ if has_committed_file?(file)
74
+ # TODO warn if there are unstaged changes.
75
+ `git show #{ref}:#{file}`
76
+ else
77
+ EY.ui.warn <<-WARN
78
+ Warn: #{file} is not committed to this git repository:
79
+ \t#{root}
80
+ This can prevent ey deploy from loading this file for certain server side
81
+ deploy-time operations. Commit this file to fix this warning.
82
+ WARN
83
+ root.join(file).read
84
+ end
85
+ end
86
+
87
+ def current_branch
88
+ ensure_repository!
89
+ branch = `git symbolic-ref -q HEAD`.chomp.gsub("refs/heads/", "")
90
+ branch.empty? ? nil : branch
91
+ end
92
+
93
+ def remotes
94
+ ensure_repository!
95
+ @remotes ||= `git remote -v`.scan(/\t[^\s]+\s/).map { |c| c.strip }.uniq
96
+ end
97
+
98
+ def fail_on_no_remotes!
99
+ if remotes.empty?
100
+ raise EY::Repo::NoRemotesError.new(root)
101
+ end
102
+ end
103
+
104
+ end # Repo
105
+ end # EY
@@ -0,0 +1,159 @@
1
+ require 'escape'
2
+ require 'net/ssh'
3
+ require 'engineyard-serverside-adapter'
4
+
5
+ module EY
6
+ class ServersideRunner
7
+ def initialize(options)
8
+ @verbose = options[:verbose] || !!ENV['DEBUG']
9
+ @hostname = options[:bridge]
10
+ env = options[:environment]
11
+ @adapter = load_adapter(@hostname, options[:app], env, @verbose, options[:serverside_version])
12
+ @username = env.username
13
+ @hierarchy_name = env.hierarchy_name
14
+ @command = nil
15
+ end
16
+
17
+ def deploy(&block)
18
+ @command = @adapter.deploy(&block)
19
+ self
20
+ end
21
+
22
+ def rollback(&block)
23
+ @command = @adapter.rollback(&block)
24
+ self
25
+ end
26
+
27
+ def restart(&block)
28
+ @command = @adapter.restart(&block)
29
+ self
30
+ end
31
+
32
+ def put_up_maintenance_page(&block)
33
+ @command = @adapter.enable_maintenance(&block)
34
+ self
35
+ end
36
+
37
+ def take_down_maintenance_page(&block)
38
+ @command = @adapter.disable_maintenance(&block)
39
+ self
40
+ end
41
+
42
+ def call(out, err)
43
+ raise "No command!" unless @command
44
+ @command.call do |cmd|
45
+ run cmd, out, err
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def load_adapter(bridge, app, environment, verbose, serverside_version)
52
+ EY::Serverside::Adapter.new("/usr/local/ey_resin/ruby/bin") do |args|
53
+ args.serverside_version = serverside_version
54
+ args.app = app.name
55
+ args.git = app.repository_uri
56
+ args.instances = instances_data(environment.deploy_to_instances, bridge)
57
+ args.stack = environment.app_server_stack_name
58
+ args.framework_env = environment.framework_env
59
+ args.environment_name = environment.name
60
+ args.account_name = app.account.name
61
+ args.verbose = verbose
62
+ end
63
+ end
64
+
65
+ # If we tell engineyard-serverside to use 'localhost', it'll run
66
+ # commands on the instance directly (#system). If we give it the
67
+ # instance's actual hostname, it'll SSH to itself.
68
+ #
69
+ # Using 'localhost' instead of its EC2 hostname speeds up
70
+ # deploys on solos and single-app-server clusters significantly.
71
+ def instances_data(instances, bridge)
72
+ instances.map do |i|
73
+ {
74
+ hostname: i.hostname == bridge ? 'localhost' : i.hostname,
75
+ roles: [i.role],
76
+ name: i.name,
77
+ }
78
+ end
79
+ end
80
+
81
+ def run(remote_command, out, err)
82
+ cmd = Escape.shell_command(['bash', '-lc', remote_command])
83
+
84
+ if cmd.respond_to?(:encoding) && cmd.respond_to?(:force_encoding)
85
+ out << "Encoding: #{cmd.encoding.name}" if @verbose
86
+ cmd.force_encoding('binary')
87
+ out << " => #{cmd.encoding.name}; __ENCODING__: #{__ENCODING__.name}; LANG: #{ENV['LANG']}; LC_CTYPE: #{ENV['LC_CTYPE']}\n" if @verbose
88
+ end
89
+
90
+ out << "Running command on #{@username}@#{@hostname}.\n"
91
+ out << cmd << "\n" if @verbose || ENV['PRINT_CMD']
92
+
93
+ if ENV["NO_SSH"]
94
+ out << "NO_SSH is set. No output.\n"
95
+ true
96
+ else
97
+ begin
98
+ ssh(cmd, @hostname, @username, out, err)
99
+ rescue Net::SSH::AuthenticationFailed
100
+ raise EY::Error, <<-ERROR
101
+ Authentication Failed. Things to fix:
102
+ 1. Add your SSH key to your local SSH agent with `ssh-add path/to/key`.
103
+ 2. Add your SSH key to #{@hierarchy_name} on Engine Yard Cloud and apply the changes.
104
+ (https://support.cloud.engineyard.com/entries/20996846-set-up-ssh-keys)
105
+ ERROR
106
+ end
107
+ end
108
+ end
109
+
110
+ def net_ssh_options
111
+ level = :fatal # default in Net::SSH
112
+ if debug = ENV["DEBUG"]
113
+ level = :info
114
+ if %w[debug info warn error fatal].include?(debug.downcase)
115
+ level = debug.downcase.to_sym
116
+ end
117
+ end
118
+
119
+ {paranoid: false, verbose: level, keepalive: true, keepalive_interval: 60}
120
+ end
121
+
122
+ def ssh(cmd, hostname, username, out, err)
123
+ exit_code = 1
124
+ Net::SSH.start(hostname, username, net_ssh_options) do |net_ssh|
125
+ net_ssh.open_channel do |channel|
126
+ channel.exec cmd do |_, success|
127
+ unless success
128
+ err << "Remote command execution failed"
129
+ return false
130
+ end
131
+
132
+ channel.on_data do |_, data|
133
+ out << data
134
+ end
135
+
136
+ channel.on_extended_data do |_, _, data|
137
+ err << data
138
+ end
139
+
140
+ channel.on_request("exit-status") do |_, data|
141
+ exit_code = data.read_long
142
+ end
143
+
144
+ channel.on_request("exit-signal") do |_, data|
145
+ exit_code = 255
146
+ end
147
+
148
+ # sending eof declares no more data coming from this end (close stdin)
149
+ channel.eof!
150
+ end
151
+ end
152
+
153
+ net_ssh.loop
154
+ end
155
+ exit_code.zero?
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,6 @@
1
+ module EY
2
+ module Templates
3
+ end
4
+ end
5
+
6
+ require 'engineyard/templates/ey_yml'
@@ -0,0 +1,196 @@
1
+ # Engine Yard Cloud Deploy Options
2
+ #
3
+ ####################################################################
4
+ # IMPORTANT
5
+ # Commit this file into your git repository.
6
+ # These options are loaded on the server during deploy.
7
+ ####################################################################
8
+ #
9
+ # Valid locations:
10
+ # * REPOSITORY_ROOT/config/ey.yml.
11
+ # * REPOSITORY_ROOT/ey.yml
12
+ #
13
+ # Further information available here:
14
+ # https://support.cloud.engineyard.com/entries/20996661-customize-your-deployment-on-engine-yard-cloud
15
+ #
16
+ # For advanced usage, see the source that loads this configuration:
17
+ # https://github.com/engineyard/engineyard-serverside/blob/master/lib/engineyard-serverside/configuration.rb
18
+ #
19
+ defaults:
20
+ # Run migrations during deploy by default.
21
+ #
22
+ # When set to true, runs the migration_command (below) during deploy.
23
+ #
24
+ # This setting can be overridden for individual deployments using
25
+ # the command line options --migrate or --no-migrate.
26
+ #
27
+ <%= option 'migrate' %>
28
+
29
+ # Default migration command to run when migrations are enabled.
30
+ #
31
+ <% if existing_config['migration_command'] || config['migrate'] %>
32
+ <%= option 'migration_command' %>
33
+ <% else %>
34
+ <%= commented_option 'migration_command' %>
35
+ <% end %>
36
+
37
+ # Enables rails assets precompilation always and halts when the task fails.
38
+ #
39
+ # By default, assets are detected using app/assets and config/application.rb.
40
+ #
41
+ # If you use rails assets and you want Engine Yard to compile your assets
42
+ # during deploy, set this to true. If you want to compile assets locally
43
+ # before deploy, set this to false. Make sure you add `public/assets` to
44
+ # `.gitignore` if you want Engine Yard to precompile your assets.
45
+ #
46
+ # For more control over assets, set precompile_assets: false and
47
+ # run your precompile task in the deploy/before_compile_assets.rb deploy hook.
48
+ #
49
+ <%= option 'precompile_assets' %>
50
+
51
+ # Override the assets:precompile rake task. This option will be used instead
52
+ # of <%= defaults['precompile_assets_task'] %> in the `rake <%= defaults['precompile_assets_task'] %>` command.
53
+ #
54
+ <%= option_unless_default 'precompile_assets_task' %>
55
+
56
+ # Asset strategies affect the way assets are stored on the server.
57
+ #
58
+ # * private
59
+ # Store assets directly in public/assets for each deployment.
60
+ # Previous assets are symlinked for continuity.
61
+ # When assets are reused, they are copied using rsync.
62
+ #
63
+ # * shifting
64
+ # Assets are kept in a shared directory on each server.
65
+ # When new assets are compiled, old assets are shifted to a shared
66
+ # last_assets directory. This has always been the default behavior.
67
+ #
68
+ # * shared
69
+ # Assets are kept in a shared directory on each server.
70
+ # When new assets are compiled, the same directory is used.
71
+ # Assets will accumulate in this mode if a cleaning script is not run.
72
+ # Use this strategy if you want to write your own asset cleaning script.
73
+ #
74
+ # * cleaning
75
+ # Like shared, but a cleaning script is run before each new compile.
76
+ # The script attempts to remove all files not mentioned by the old
77
+ # manifest.yml, before it is replaced by the new manifest (leaving 2
78
+ # deployments worth of assets in the directory)
79
+ #
80
+ # "private" is recommended because it is the least error prone.
81
+ # If you prefer faster compilation, "shared" can be quicker, but will require
82
+ # custom scripting and will cause problems when rollbacks are used.
83
+ # "shifting" is the default behavior.
84
+ #
85
+ <% if config['asset_strategy'] != defaults['asset_strategy'] %>
86
+ <%= option 'asset_strategy' %>
87
+ <% else %>
88
+ #asset_strategy: private
89
+ <% end %>
90
+
91
+ # This list of repository relative paths is checked for changes during
92
+ # each deployment (when change detection is not disabled). If `git diff`
93
+ # detects changes since the last deployment, fresh assets will be compiled.
94
+ #
95
+ # This option overrides the default list, so include the following
96
+ # defaults if you need them.
97
+ #
98
+ <% if config['asset_dependencies'] %>
99
+ <%= option 'asset_dependencies' %>
100
+ <% else %><%# better default format than the normal yaml dump %>
101
+ #asset_dependencies:
102
+ #- app/assets # default
103
+ #- lib/assets # default
104
+ #- vendor/assets # default
105
+ #- Gemfile.lock # default
106
+ #- config/application.rb # default
107
+ #- config/routes.rb # default
108
+ #- config/requirejs.yml # example of a custom asset dependency
109
+ <% end %>
110
+
111
+ # When true, precompiles assets even if no changes would be detected by
112
+ # running git diff with the asset_dependencies above.
113
+ #
114
+ # Default is false (always check git diff before asset compilation)
115
+ #
116
+ <%= option_unless_default 'precompile_unchanged_assets' %>
117
+
118
+ # Choose which servers should compile assets.
119
+ #
120
+ # Default behavior is to exclude util instances.
121
+ # Specify :all to compile on all servers including util servers.
122
+ #
123
+ <% if config['asset_roles'] %>
124
+ <%= option 'asset_roles' %>
125
+ <% else %>
126
+ #asset_roles: :all
127
+ <% end %>
128
+
129
+ # Bundle without different bundler groups:
130
+ # Ex: bundle install --without '[bundle_without]'
131
+ #
132
+ # Default is "<%= defaults['bundle_without'] %>".
133
+ # Leave blank to remove --without from the bundle install command.
134
+ #
135
+ <%= option_unless_default 'bundle_without' %>
136
+
137
+ # Add extra options to the bundle install command line.
138
+ # Does not override bundle_without, if specified.
139
+ #
140
+ # If the application's gems are vendored in the
141
+ # repository, setting --local can speed up bundle.
142
+ #
143
+ <%= option_unless_default 'bundle_options' %>
144
+
145
+ # Enable maintenance page during migrate action (default)
146
+ # Setting this to false, disables maintenance page during migrations.
147
+ #
148
+ # CAUTION! No-downtime migrations requires careful migration
149
+ # planning. Migrations must be non-destructive. The *previous*
150
+ # deployment might serve pages during a partially migrated state.
151
+ # For example, if you rename a column, all traffic served during
152
+ # that migration will be broken until the new code is deployed.
153
+ #
154
+ <%= option_unless_default 'maintenance_on_migrate' %>
155
+
156
+ # Enable maintanence page during every deploy.
157
+ # Unicorn and Passenger support no-downtime deploys, so the default
158
+ # for these servers is false. Mongrel and some other servers default
159
+ # to true to avoid downtime during server restarting.
160
+ #
161
+ <%= option_unless_default 'maintenance_on_restart' %>
162
+
163
+ # If true, always run deployments in verbose mode.
164
+ #
165
+ <%= option_unless_default 'verbose' %>
166
+
167
+ # Hide the warning shown when the Gemfile does not contain a recognized
168
+ # database adapter (mongodb for example)
169
+ #
170
+ # This warning is here to help new customers that accidentally have no adapter.
171
+ # You may safely set this to true if you aren't using a common database.
172
+ #
173
+ <%= option_unless_default 'ignore_database_adapter_warning' %>
174
+
175
+ # You can add custom keys that will be available in your deploy hooks.
176
+ # Custom keys will be available using config.key or config[:key]
177
+ #
178
+ #your_own_custom_key: custom info
179
+
180
+ <%= extra_root_options %>
181
+
182
+ ####################################################################
183
+ # Environment specific options.
184
+ #
185
+ # The options you specify here will only apply to a single environment
186
+ # that exactly matches the environment name key.
187
+ #
188
+ # Environment options will override the default options above.
189
+ #
190
+ environments:
191
+
192
+ # These options will only apply to the EXAMPLE_ENVIRONMENT environment.
193
+ #EXAMPLE_ENVIRONMENT:
194
+ #precompile_unchanged_assets: true
195
+
196
+ <%= environment_options %>