Incorporating git Version in a Rails App
Problem
Quick: what is the version of your server?
If you run Ruby on Rails, I know it’s 5.1.6 (the version I use), but what about the version of the added code to the Ruby on Rails framework? Do you: go to your server and ‘poke’ for every feature and fix?
What if you use awesome continuous delivery systems like Kubernetes or Docker swarm and there are multiple instances of your server? How would you know?
How do you know the version you are seeing on the server corresponds to the version in your source control system?
One solution I have used to solve this problem: use the version of the server code to the version your source control system uses. Have a direct link between development and production deploys.
For me, I use git and with Ruby on Rails, that means incorporating the server version in an endpoint.
git version tracking
git tracks the version of code with SHA1. A small change will create a new value of the SHA, even a new branch will as well.
Incorporating this value from development through to production deployment will ensure we know the version the server is running and can track back to source control.
This value automatically generated at the development level. So we just have to incorporate this value in our pipeline.
Let’s get this value into a Ruby on Rails app to start displaying the git version in your Ruby on Rails app.
Requirements
If you would like to follow along in this document, these are the items needed:
- Ruby - any version, but I am using 2.5.1p57
- Ruby on Rails - any version, but I am using 5.1.6
- git - any version, but I am using 2.14.3
Source code for this article is here
New Rails Project
First, let’s create a brand new Ruby on Rails project using Ruby.
$ ruby -v # 2.5.1p47
$ gem install bundler
... # gem install message for bundler
$ echo "ruby '2.5.0'\n source 'https://rubygems.org'\n gem 'rails', '~> 5.1.0'\n" > Gemfile
$ bundle install
... # bundler install messages for rails
$ rails new versioning
... # rails new messages
Do a quick test to make sure things are working:
$ cd rails-git-versioning
$ rails s
In another terminal window:
$ curl localhost:3000
... # lots of output
<p class="version">
<strong>Rails version:</strong> 5.1.6<br />
<strong>Ruby version:</strong> 2.5.1 (x86_64-darwin16)
</p>
</section>
</div>
</body>
</html>
At this point, we confirmed there’s a new Ruby on Rails project running.
New Controller
I will add a new controller that will just respond with the server version.
Let’s create a new controller with command:
$ rails generate controller version
In the app/controllers/version_controller.rb
file, insert the
following contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
class VersionController < ApplicationController
def index
branch = 'temp_branch'
sha = 'temp_sha'
version = {
branch: branch,
sha: sha
}
render json: version
end
end
temp_branch
and temp_sha
are just placeholders for now. I want to
make sure the server responds the way I expect. Since I’m only doing
manual testing for this, I want to get other components set up right
away.
In the config/routes.rb
file, ensure the contenst are:
1
2
3
4
5
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get 'version', to: 'version#index'
end
Basically, add line 4, but I have the whole file here as reference.
Test Endpoint
Let’s run the server and test out the response from the new /version
endpoint:
$ rails s
In another terminal, run the following command:
$ curl http://localhost:3000/version
{ "branch": "test_branch", "sha": "test_sha" }
If this is not working, go back and check the app setup. This is a crucial point as debugging will be the hardest here and the next steps will be automatic.
Please contact me if you need help.
Incorporating git version
With the server returning the version from the /version
endpoint,
let’s get a real git branch and the sha version in there.
We could just “copy it by hand”, but someone needs to remember to do it every time there’s a code change.
A niftier way would be to directly read the git information from the Rails server.
Modify the branch
and sha
variables to be:
branch = `cat .git/HEAD | awk '{ print $2 }'`.strip.split('/')[2..-1].join('/')
sha = `cat .git/HEAD | awk '{ print $2 }' | xargs -I % sh -c 'cat .git/%'`.strip
Key items used:
.git | this is the directory which git keeps the metadata for the project |
.git/HEAD | this file keeps track of what the working branch is |
.git/refs/heads/ |
this has the current sha of the branch |
Instead of having the work done by ruby to get the branch & sha values, I use common UNIX utilities: cat, awk, and xargs. This keeps the code portable to any UNIX system.
Alternative
Another way to get this value, using the git
command would be the
following commands:
$ git symbolic-ref --short HEAD # branch name
$ git rev-parse HEAD # sha value
This is better because if there’s any changes to the .git
directory,
this will return the right values.
On any system where the git
command is not included, parsing the
.git
directory will still get the right value.
/version
returns git version?
Let’s test this out again, with the server running, execute the following command:
$ curl http://localhost:3000/version
{ "branch": "master", "sha": "66650149ce6a972a6d7a54e240206505f004adb5" }
Create New Branch
Let’s make a new branch and see what the new result is:
$ git checkout -b feature/new_version
$ curl http://localhost:3000/version
{ "branch": "feature/new_version", "sha": "9fd5074bab721b5757305e2944e352434290e96c" }
The server did not even have to restart! Isn’t that nifty?!
Conclusion
Using git as a way to keep track of the version your server is running is easy as adding a new endpoint that grabs the git version.
Having this version will show what version the server is running at any time and is trackable right back to the source code.
I highly recommend this to avoid poking and prodding your server to figure out which version is running, especially when things are on fire.