Do something after Heroku deployment - ruby-on-rails

Is there a hook or event that can be used in order to trigger something immediately after a Rails app is deployed to Heroku?
Specifically, we want to build out a certain directory structure under tmp/ (the structure is contingent on config values so it can't be checked into source).

Heroku supports a few after deploy hooks - see https://devcenter.heroku.com/articles/deploy-hooks .
You can use the HTTP hook to trigger an action on your app which will create your desired structure. As an example of how to use this HTTP deploy hook, check out http://dzello.com/blog/2012/03/11/spin-up-your-heroku-dyno-automatically-after-deploy/
Keep in mind though that if you build anything in the tmp directory, it'll go away after a dyno restart.

Turns out the two parts of my question are not compatible- #eugen explains how to implement a post-deploy hook, but also reminded me that any tmp directories I create will be blown out whenever a dyno restarts...
So what's the solution? The following is what I came up with:
config/initializers/setup.rb
# Ensure temp file system is set up:
begin
Dir.mkdir("tmp/customdir") unless File.exists?("tmp/customdir")
rescue StandardError => e
# Allow this to fail on initial deploy
# (It will raise error: "No such file or directory - tmp/customdir" because the tmp dir won't yet exist)
end
Explanation:
Heroku essentially does a two-pass initialization during deploys. The first pass, when it pre-compiles assets, would normally choke on the above code because the tmp directory will not yet exist. If we allow it to fail, it will work on the second pass, and on subsequent restarts.
Yeah it's a hack. I've tried before to figure out how to distinguish between a pre-compile initialization and normal initializations, but there doesn't seem to be a way, so this is what we're left with.
If anyone can improve on this would love to hear it.

Related

Rails 5 Upgrade: routes.rb file cleared out

I am upgrading a Rails app from 4.2.x to 5.0.x. After I updated all my Rails-related gems in Gemfile, I ran the rails task for updating all my files to conform to the newest version, as per the upgrade guide:
rails app:update
There were many conflicts in this command, so I pressed a to accept all conflicts and then review them manually before committing them. My main concern is what happened to the config/routes.rb file. Basically, the entire contents of the file, save for the Rails.application.routes.draw block and a single comment about the DSL added to the end of the file, were kept. All routes that have been added to the app over the years were cleared out, not to be found in any other file.
This issue doesn't block me, I'll simply checkout the file to bring it back to its former state. However, what especially concerns me is that an essential file was cleared out in what appears to be a normal situation for upgrading a Rails version. I'd like to see if anyone else has run into this issue, whether this is expected, what's going on. Comments from Rails maintainers are welcome. Thank you.
The app:update task is just a slightly modified version of the task that creates a new Rails application. It writes out files under config/ and bin/ based on the templates from the new Rails version. If you selected a to accept all conflicts, then that simply means that Rails is going to overwrite any differing files with its own copy without asking you. This is normal and expected. If you don't want that behavior, then don't press a.
config/routes.rb is almost certainly the one file under config/ that will differ almost completely from the empty boilerplate file.
Realistically, Rails can't be expected to parse your custom changes and merge them with a new template.
For what it's worth, I like to start out doing exactly what you did, with a clean working directory just let the app:update task overwrite anything it wants to, and then go through all the changes with a side-by-side, SCM-aware diff tool like vim-fugitive to stage or discard the differences.
Well, just don't accept all conflicts, take a look in each one of it. Making version upgrades of 1 level of magnitude may change a lot of things and the process takes a while.
First take a look on the changes in the framework and do the process carefully. You may be breaking a lot more stuff that you are not noticing:
http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-4-2-to-rails-5-0

Rails 5: where to put start-up check that doesn't trigger unless serving the app?

I was hoping to put in some sanity checks on Rails (v5) application startup to ensure that all necessary ENV variables / configuration was properly set.
However, I want to run these checks only if I'm actually running the server. I don't want it happening any time the Rails environment is loaded, e.g., running tests, asset precompilation (including with RAILS_ENV=production), rails c, etc.
Is there a place in the Rails start-up chain that I could insert those sanity checks without affecting all Rails-related tasks?
This is an interesting challenge. The problem is that (virtually) the entire application is loaded regardless of whether you're running tests, pre-compiling assets or anything else. The only thing that's different is that you have the process residing in memory.
Have you considered creating a production validation task that you run in your deployment process prior to launching the server process?
EDIT : Additional Thoughts
Looking at the rails server command, it calls an instance of Rails::Server, a subclass of Rack::Server (whereas rails console calls an instance of Rails::Console).
You have two choices:
Task (Best Choice)
Create a rake task that you execute during your deployment process prior to starting the server process. It should raise an error if the configuration is not valid, which should halt your deployment process.
I strongly suggest this approach. It's self-contained, you can test it far easier, and you don't have to meddle with the internals of the server process.
Monkey Patch Rails::Server#start
Monkey patch Rails::Server (probably Server#start) so it runs your validation logic if it's running in a production environment. If it doesn't, throw an error. This is ugly, you won't be able to test it easily, and it violates my sense of order :P
I know you don't want this logic to run in production environment in non-server use cases but my production deployments are always configured completely regardless of what I do with them. I've been debating setting up a validation step myself.

Rails, Cucumber, Capybara, Selenium, Pow, domains, subdomains and javascript

TL:DR - How can I make Cucumber request a page through the app but pretend that the request came from "http://mysubdomain.mydomain.dev", and run the javascript properly?
Ok, this may take a while to explain, but please bear with me...
I'm a 'relatively' experienced Rails dev, but only recently took the plunge into full BDD/TDD.
The page I'm testing in cucumber has some javascript in it for creating a new nested object (very similar to this RailsCast).
Now the problem lies in the fact that not only do I have variable sub-domains, I have variable Domains too (Just trust me on that one...)
So the app needs to query the request.host to find the current_domain and the current_subdomain before everything ties up in the background and it can carry on serving the right bits of the app.
I've managed to get all of the tests passing fine using the host! domain and Capybara.default_host = domain techniques in the background steps.
However, when I use the #javascript tag on the feature that tests a page with .js on it, I get Firefox grabbing focus, then trying to load the full url.
Now it also happens that I'm running Pow, and have those urls hooked up to the dev server. Unsurprisingly, they don't work as when the user tries to log in, it's looking in the dev DB. I tried running the suite after turning the pow server off, and it just timed out.
Surely the javascript webdriver shouldn't be actually accessing the url, but simply running the app itself and pretending that the host is what I tell it to be???
I'm obviously missing something here - How can I make Cucumber build the page internally, but pretend that the request came from "http://mysubdomain.mydomain.dev"?
edit: Jason - the variable domain trick is achieved exactly the same as subdomains... If you can query the db for an account based on request.subdomains.first, you can query for a domain through request.domain. You just have to double check a few things like capitalisation etc. to minimize the risk of a malformed url breaking stuff, and obviously you need to ensure that the domain record exists in the db first...
Oh - and cache the domain record requests with care...
It means that you can serve the same app, but with different styling and landing pages etc. Useful for PaaS apps with a broad customer base - you can re-brand it and sell it as a specific solution to one group's problems, even though it's the same guts underneath.
I am looking to do something very similar. I thought about adding an additional pow directory for test, then using the pow directives to override the environment. I think this is done within a ".powenv" inside your app directory. Here is an example of a quick fix:
echo export RAILS_ENV=cucumber > .powenv && touch tmp/restart.txt
But it would be better to do something dynamic so that before you start the test, you can tell pow what env to run, then when finished switch back, maybe even run the test server on a different port on a temp basis. Pow is the only great solution I know of so far for handeling subdomains.
EDITED: I have this working now in my environment with the following addition to my features/support/env.rb file.
# Switch Pow to For Cucumber Tests
Capybara.default_driver = :selenium # Subdomain testing will only work with pow and selenium
pow_config = "#{Rails.root}/.powenv" # Dont change, this is the Config Files Location.
pow_config_stash = "#{Rails.root}/.powenv_original" # This is what the config will be stashed as during testing.
Before do
# Set the default host
Capybara.app_host = "http://www.resipsa.dev"
# Stash the existing config
File.rename(pow_config,pow_config_stash) if File.exists? pow_config
# Write the new pow config
f = File.new("#{Rails.root}/.powenv", "w")
f.write "export RAILS_ENV=test"
f.close
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end
After do
# Delete the temp config
File.delete(pow_config)
# Restore the Original Config
File.rename(pow_config_stash,pow_config) if File.exists? pow_config_stash
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end

Recompiling Sass assets in production

I'm working on a rails app where we want to allow the user to use an admin tool to create new themes. The admin tool is a separate application and communicates with our main application through a database. My problem is that I've written custom Sass extensions to load our data into our style sheets, but once that is done, I am unable to recompile the assets in our production environment.
So far I've seen two possibilities for this:
1.Increment the version of config.assets.version. So I have this code:
MyApp::Application.assets.version =
(MyApp::Application.config.assets.version.to_i + .1)
From what I've read incrementing this should cause the assets to recompile, but it seems to only work when it is incremented by hand and the server is restarted.
2.Create a compiler and tell it to clean up the old assets and recompile them:
compiler = Compass::Compiler.new(
Rails.root.to_s,
Compass.configuration.sass_path,
Compass.configuration.css_path,
{:sass => Compass.sass_engine_options} )
compiler.clean!
compiler.run
With this method, however, I run into the problem that the Sprockets::Index.expire_index! method raises an error when I try to create a new compiler.
Yes, I do understand that I can set the assets to recompile on every request, but the performance hit is not something we want. Also, since this is a theme, the data should not be changing often, so we only need to recompile when the admin chooses to save the new theme.
So, finally, my question is: Are there any other possible methods to do what I want? Or am I going down the right path, and if so, where am I going wrong?
EDIT:
I forgot to mention that since we are using Sass functions to change the values of the stylesheets, even if I do turn on the option to compile in production, it won't work. Since the actual stylesheets will never change.
Rails has a Rake task that does asset compilation for you. You should run it once every time you deploy your application to your production environment.
rake assets:precompile
The compiled assets are output to public/assets. For more details, check out the Rails Asset Pipeline Guide.

How to efficiently handle the changes between production and development when updating from the repository

I have inherited a project with a local development environment that has code specific to that machine, and which is different for the production server. Even though the majority of it is contained in constants and the rest is in the tests, every time I commit from development and update in production I'm going to have to make the same changes in production. Fortunately this is an internal tool with low volume.
I guess I could write a script to automate it but I'm hoping there's a better solution. Anyone else solved this problem?
These questions are similar but not asking the same thing, just so you know I looked:
(1) make changes to a production database
(2) transferring changes from dev to prod
Edit: Nelson LaQuet put me on what I believe is the right track, which led me to Configuring Rails Applications. However, I am unsure how to reference my FormController < ApplicationController constants, such as MyExternalCodeDir, in config/environments/production.rb and config/environments/development.rb.
Also I do not want to be required to change every reference to MyExternalCodeDir to something like config.MyExternalCodeDir.
You abstract all environment settings (database connection/pathing/URIs) into a single file. Let's call it "config.ini"
Then you simply commit a "template" called "config.ini.template" that contains the structure of the config file clearly documenting what is expected at each value - and sensable defaults. You then commit this file.
After you do that, delete the current config.ini file that is specific to your location, and add it to svn:ignore. Now, when you copy and paste config.ini.template to config.ini, and change your settings, it is not going to be committed to the repository.
It adds an extra step per deployment, but must be done only once (unless you add/remove config options). This is the best and most standard way of accomplishing what you want.
I would move the constants' values that are environment specific in a configuration file, which would make it easier to handle. I would also keep the code in just one repository in the version-control system and manage the build outputs in two separate repositories: one for the test environment and one for production. That way I can manage my code base however I choose, and when I want to deploy I'll first commit to test, and then merge from test to production and at that point just diff the configuration file and keep the correct configuration for the production environment.

Resources