Heroku: Display Git Revision Hash and Timestamp in Views? - ruby-on-rails

Let's say I have a Rails application deployed on Heroku. How can I display these pieces of information in my views?
The Git hash for the last revision
The Timestamp for the last revision

Heroku sets an environment variable with the commit hash ENV['COMMIT_HASH'].
As for the timestamp, you could hit the github api with the hash if you host your code there. Looks like the ruby-github gem can help you with this, or you could do it yourself with HTTParty.

The reason for this is because when your app is deployed onto the dyno grid to serve requests, it's compiled into a "slug" for fast deployment, and this slug doesn't have the git repo with it anymore.

Another way to do it is to deploy with a rake task that gets the version info you want from the local repo and updates an environment variable on the Heroku side. Then you can use a tag, or a commit hash, or anything else, without having to rely on behaviors on the Heroku side.
For example, if you wanted to use the latest tag, in your rake task:
def app_version
%x[git describe --tags --abbrev=0].strip
end
Then in the body of your task:
run "git push blah:blah blah"
run "heroku config:add APP_VERSION=#{app_version}"
I would like to be able to get that info straight from git on Heroku, rather than sneaking it in indirectly, but I've never been able to figure out how to do that.

There is grit installed on Heroku. So you can open the repository there using it.
repo = Repo.new(Rails.root + '.git')
last_commit = repo.commits.first
p last_commit.id
p last_commit.authored_date

I think you need to config.gem 'grit' into your Rails app in order to be able to create the Repo object.
You can read about grit here http://github.com/mojombo/grit/

Related

What is the best workflow for updating / deploying a Rails app through Git?

I just deployed my first Ruby on Rails app on a VPS at Digital Ocean.
To get started quickly, I did this by simply dragging my Rails directory tree (and its containing files) onto the server via (S)FTP.
I know this isn't the best solution in the long run. So how can I link my app on the server to my git repository at GitHub?
Ideally, when I work on my app locally, and then git commit and git push to my git repository, my app on the VPS will also get updated automatically.
How can this be achieved or what is the best strategy to achieve this?
Since I am building this app just by myself, I can probably keep things simple and stick to a single master branch, rather than having multiple branches.
Thanks for any help.
If I were you, I'd do the pulling and updating on the remote manually. Sorry, but this is not only best practice, but will also force you to learn something useful about system administration and don't require you to be dependent on one host, but can switch service provider and setup as easy it is to make a git-clone somewhere else.
So my workflow would be:
Client:
# Do some changes, commit and add a nice message
$ git commit myfiles
# Push to remote once I'm happy.
$ git push
# SSH to server, and continue from there.
$ ssh username#server
Server:
# Enter project directory
$ cd /var/www/myproject
# Pull code
$ git pull
Done. Or perhaps finish by refreshing server container (uWSGI, fcgi, gunicorn, what have you...)
Reading other similar answers, they hint to looking at the following resource using Capistrano:
Capistrano documentation at GitHub
You should spend a little time now setting up deploys with some automation. Since you are using rails, you should try Capistrano Gem
Capistrano will help you deploy and maintain your application with just a few simple commands. The Readme will show you how to get started, but in general, you will add the Gem by adding this to your Gemfile:
gem 'capistrano', '~> 3.2.0'
then run bundle install to install Capistrano into your bundle. If you are not already using bundler, you should start.
then run bundle exec cap install to setup your local repo for Capistrano.
Basically now you have a nice structure for deployment scripts as part of your repo. You will have to write some deploy scripts now, or modify the examples.
Once done, Capistrano will help you deploy new code (once committed and pushed to your remote repo) and restart the services.
It depends on whatever service you are using to publish your app. Depending on the provider, they may or may not provide rails service. For example, a site like Heroku, where you can actually host for free up until certain restrictions is accessible via github and you can do exactly what you're saying and just push up and publish.
If you can put your repo on your server, you can set up post-receive hooks to pull your branch into your web app directory.
To do so you would create a bare repo on your server, add it as a remote on your development machine, and then (on your server) create the file /my/app.git/hooks/post-receive and add these lines:
#!/bin/bash
#CONFIG
LIVE="/home/saintsjd/www"
read oldrev newrev refname
if [ $refname = "refs/heads/master" ]; then
echo "===== DEPLOYING TO LIVE SITE ====="
unset GIT_DIR
cd $LIVE
git pull origin master
echo "===== DONE ====="
fi
Code from Automated Deployment of PHP Applications using git, by Jon Saints.
Note that it would be possible to do something like this without putting the repo on your server if you use Github's webhooks (https://developer.github.com/v3/repos/hooks/).
However, I would highly recommend using Capistrano (https://github.com/capistrano/capistrano), which can deploy your application code and help you with a lot of administrative tasks (like restarting the server, etc.).
If you want to stick relatively close to git, you might also check out the git-deploy gem.

Rails 3 and Heroku: automatically "rake db:migrate" on push?

I have a slight annoyance with my heroku push/deploy process, which otherwise has been a joy to discover and use.
If i add a new migration to my app, the only way i can get it up onto the heroku server is to do a push to the heroku remote. This uploads it and restarts the app. But it doesn't run the migration, so i have to do heroku rake db:migrate --app myapp, then heroku restart --app myapp. In the meantime, the app is broken because it hasn't run the migrations and the code is referring to fields/tables etc in the migration.
There must be a way to change the deployment process to run the rake db:migrate automatically as part of the deploy process but i can't work it out.
Is it something i set in a heroku cpanel? Is it an option i pass to heroku from the command line? Is it a git hook? Can anyone set me straight? thanks, max
Heroku now has the ability to handle this as part of their "release phase" feature.
You can add a process called release to your Procfile and that will be run during each and every deploy.
Rails >= 5 Example
release: bundle exec rails db:migrate
Rails < 5 example
release: bundle exec rake db:migrate
What about this simple command chaining solution:
git push heroku master && heroku run rake db:migrate
It will automatically run the migrate as soon as the first one finishes successfully. It's tipically 1-2 seconds delay or less.
Here is a rake task that wraps up everything into a one-liner (and also supports rollback):
https://gist.github.com/362873
You still might wind up deploying on top of your boss's demo, but at least you don't waste time typing between the git push and the rake db:migrate.
I created a custom buildpack that gets Heroku to run rake db:migrate for you automatically on deployment. It's just a fork of Heroku's default Ruby buildpack, but with the rake db:migrate task added.
To use it with your app you'd do this:
heroku config:set BUILDPACK_URL=https://github.com/dtao/rake-db-migrate-buildpack
Also note that in order for it to work, you need to enable the user-env-compile Heroku Labs feature. Here's how you do that:
heroku labs:enable user-env-compile
And here's my evidence that this works:
Perhaps you could try separating your schema commits (migrations, etc.) commits from code commits (models, validations, etc.).
(Note the following assumes your migration changes are NOT destructive, as you've indicate covers most of your use cases.)
Your deploy process could then be:
Push schema changes to Heroku
migrate
Push application code to Heroku
This is of course far form optimal, but is an effective way to avoid downtime in the situation you've described: by the time the app receive the code for the dynamic fields, the DB will already have migrated.
(Of course, the simplest solution would be to simply push and migrate while your boss is out to lunch ;-D)
Otherwise, even if schema modifications were carried out automatically you'd still run the risk of a request passing through right before the migrations have been run.
Just for those googling folks like me I want to give a plain solution here.
I am using Rails 4 and needed to add a simple Rake task to the deployment to heroku. As I am using the 'deploy to heroku' button in github there is no chance to run "heroku run ..." immediately after deployment.
What I did: I extended the standard Rake Task 'assets:clean' that is automatically run during a deployment to heroku. The task still runs normally but I have attached my own stuff to it's end. This is done with the 'enhance' method. In the example below I add a db:migrate because this is probably what most people want:
# in lib/tasks/assets_clean_enhance.rake
Rake::Task['assets:clean'].enhance do
Rake::Task['db:migrate'].invoke
end
I admit that this is no perfect solution. But the heroku Ruby Buildpack still does not support any other way. And writing my own buildback seemed a bit of an overkill for so simple a thing.
I use a rake task to put the app in maintenance mode, push, migrate and move it off maintenance mode.
I wrote SmartMigrate buildpack which is a simple Heroku buildpack to warn of pending migrations after a ruby build whenever new migrations detected. This buildpack is intended to be part of a Multipack that has a preceding Ruby buildpack.
With due respect to other solutions here, this buildpack has 3 advantages over those:
No need for maintenance mode
No need for out-dated ruby buildpack forks that just insert the migration at the end
No need to run migrations ALL THE TIME, a warning is only displayed if new migrations are detected since the last deployment
I think David Sulc's approach is the only one which ensures that you avoid requests getting through while the app is in a broken state.
It is a bit of a pain, but may be necessary in some circumstances.
As he stated, it does require that the db migrations are non-destructive.
However, it can be difficult to push your migrations and schema changes prior to the rest of the code, as the obvious approach ('git push heroku {revnum}') relies on you having checked the migrations in before the rest of the code.
If you haven't done that, it's still possible to do this using a temporary branch:
Create a branch, based at the git revision that you most recently pushed to heroku:
git branch <branchname> <revnum-or-tag>
Check out that branch:
git checkout <branchname>
If your db migration commits only contained migrations, and no code changes, cherry-pick the commits that contain the database changes:
git cherry-pick <revnum1> <revnum2>...
If you commited your db changes in revisions which also contained code changes, you can use 'git cherry-pick -n' which won't automatically commit; use 'git reset HEAD ' to remove the files that aren't db changes from the set of things that are going to be commited. Once you've got just the db changes, commit them in your temporary branch.
git cherry-pick -n <revnum1> <revnum2>...
git reset HEAD <everything that's modified except db/>
git status
... check that everything looks ok ...
git commit
Push this temporary branch to heroku (ideally to a staging app to check that you've got it right, since avoiding downtime is the whole point of jumping through these hoops)
git push heroku <branchname>:master
Run the migrations
heroku run rake db:migrate
At this point, you might think that you could just push 'master' to heroku to get the code changes across. However, you can't, as it isn't a fast-forward merge. The way to proceed is to merge the remainder of 'master' into your temporary branch, then merge it back to master, which recombines the commit histories of the two branches:
git checkout <branchname>
git merge master
git diff <branchname> master
... shouldn't show any differences, but just check to be careful ...
git checkout master
git merge <branchname>
Now you can push master to heroku as normal, which will get the rest of your code changes across.
In the second-to-last step, I'm not 100% sure whether merging master to {branchname} is necessary. Doing it that way should ensure that a 'fast-forward' merge is done, which keeps git happy when you push to heroku, but it might be possible to get the same result by just merging {branchname} to master without that step.
Of course, if you aren't using 'master', substitute the appropriate branch name in the relevant places above.
I've been using the heroku_san gem as my deployment tool for a while. It is a nice small, focused tool for the push + migration. It adds some other rake commands that make accessing other functions (like console) easy. Beyond not having to remember database migrations, my favorite feature is its Heroku configuration file – so I can name all my servers (production, staging, playground4, shirley) whatever I want – and keep them straight in my head.

How do I send a variable between Capistrano and Rails?

I have a Rails app deployed with Capistrano and in our Acceptance environment I want to set the page title to include the branch that is currently deployed.
The branch is set on deploy via Capistrano and I'd like to migrate that info from Cap to Rails somehow.
Obviously I can get Cap to write the branch name out to a file and read it back in Rails, but I'm hoping there's a better solution.
I've tried a couple experiments with setting default_environment but that hasn't seemed to work I'm assuming because those environment variables are only present in the shells that Capistrano creates.
Any suggestions?
Obviously I can get Cap to write the
branch name out to a file and read it
back in Rails
That's the best way, IMO
Here's a way that doesn't even need Capistrano, though it does need git. Read the branch name by capturing the output from running a git command in a Rails initializer:
# config/initializers/set_title.rb
module MyConfig
TITLE = `git symbolic-ref HEAD`.chomp.split("/").last
end
Then just refer to the constant in your template:
<title><%= MyConfig::TITLE %></title>

heroku deployment testing

I have an app I've been upgrading to Rails 3, I've been hosting it on heroku for the past 6 months (rails 2.3.8 currently) but am unclear how to test my Rails 3 branch before I proceed.
The most i could find about the subject in their docs was that I could do something like this:
$ heroku create --stack bamboo-ree-1.8.7 --remote trybamboo
Created http://young-wind-88.heroku.com/ | git#heroku.com:young-wind-88.git
Git remote trybamboo added
$ git push trybamboo master
This seems to work and creates a new app from the same git repo but with my new branch, what I'm unsure about is how to push some test data to make sure it works correctly? Ie heroku db:push... I haven't tried it, but all heroku commands still seem tied to the master branch and my production app. How can I push data to my remote app? Any ideas? Or the best way to test out new branches?
http://docs.heroku.com/bamboo
The heroku command accepts an argument --app foo to let you run commands against a specific app other than the default. Sounds like you could db:pull from your main app and then db:push into your production app. If you can figure out the database URL for the source then you could probably do it in one go.
You can create seed data in db/seeds.rb and run heroku rake db:seed after pushing your application to put this data into your application's database.

Deploying to Heroku with sensitive setting information

I'm using GitHub for code and Heroku for the deployment platform for my rails app.
I don't want to have sensitive data under Git. Such data include database file settings (database.yml) and some other files that have secret API keys.
When I deploy to heroku, how can I deal with files that are not under revision control.
When I use Capistrano, I can write some hook methods, but I don't know what to do with Heroku.
For Heroku, you'll need to have database.yml under Git because Heroku will automatically read it and create a PostgreSQL configuration from it.
For other sensitive information such as API keys, Heroku provide config vars which are effectively environment variables. You can add them using:
heroku config:add KEY=value
—and access them from within your application using:
ENV['KEY']
Note that config vars can be listed, added and removed using the heroku command-line program and that once set they are persistent.
Heroku Config Vars documentation
I would create a local branch, let's call it SECRET, and make the 'secret' modifications there. Commit them and DO NOT push them to github.
Now just checkout and keep working on the master branch till ready to release.
To prepare the release checkout the SECRET branch, merge the master branch into it, and push it to heroku as usual.
(BTW : I always forget to switch back to the work branch, git stash is your friend in this case)

Resources