Version Control – How to Track Code Versions in Different Environments

deploymentdevelopment-environmentdevelopment-processgitversion control

My team currently uses a fairly simple branching/deployment process that looks like this:

                ┌────────┐ ┌────┐ ┌──────┐
 Environments:  │  DEV   │ │ QA │ │ PROD │
                └────────┘ └────┘ └──────┘

                     ▲       ▲       ▲
                     │       │       │

                ┌────────┐ ┌────┐ ┌──────┐
 Builds:        │  DEV   │ │ QA │ │ PROD │
                └────────┘ └────┘ └──────┘

                     ▲       ▲       ▲
                     │       │       │

                ┌────────┐ ┌────┐ ┌──────┐
 Branches:      │ master │ │ qa │ │ prod │
                └────────┘ └────┘ └──────┘

Each environment has its own branch (we use git) and its own build that uses that branch. When we want to promote from one environment to another, for example, from DEV to QA, we merge the master branch into qa and kick off a new QA build (which is then automatically deployed to the QA environment).

We're considering moving to a new process that would do away with having a dedicated branch and build for each environment. Instead, a single release build would create a "deployment package" which could then be deployed to any environment. We're imagining a typical workflow would look something like this:

                ┌────────┐     ┌────┐     ┌──────┐
 Environments:  │  DEV   │ ──► │ QA │ ──► │ PROD │
                └────────┘     └────┘     └──────┘

                      ▲ 
                       \ 

                        ┌───────────────┐
 Builds:                │ release build │
                        └───────────────┘

                                ▲
                                │

                ┌────────┐ ┌─────────┐
 Branches:      │ master │ │ release │
                └────────┘ └─────────┘

Promoting from one environment to another would no longer be handled in source control; rather, we'd just take the already-built binaries (the "deployment package") and drop that on the new environment.

This new system would allow us to deploy any build to any environment, which has several advantages. For example, it's trivial to test PROD bug fixes in DEV and QA. Our current system doesn't provide an easy way to do this without rolling back a branch, which we'd obviously like to avoid.

The biggest drawback with this new system is that we no longer have an automatic way of tracking what code is in which environment. If we need to make a fix in PROD, we no longer have a dedicated branch in sync with the current production codebase. The same goes for QA – if we want to push a quick change to QA without dredging up in-progress work from master, we no longer have a branch that reflects the current state of the QA environment.

How can we keep track of what code is in each environment?

Some options we're considering:

  • utilizing git tags to keep track of which commit is in which environment
  • embedding the git commit used by the build into each deployment package

Best Answer

Git tags are what you really want to use to designate releases. The reason is that they have meaning to you and can be used to quickly recognize the linkage between the state deployed code and any information that the build server may have (such as build number).

While that is the answer you are looking for, it only solves half the problem. The other is "hey, here's the deployed .[wje]ar on the server, what build did it come from?" We know that you'll never have different versions of the application deployed on dev and qa or prod. Right?

The solution to that part of the question is to have the build server put the information into the manifest. Coming from a maven and svn example that I have in front of me:

<manifestEntries>
    <Specification-Title>${project.name}</Specification-Title>
    <Specification-Version>${project.version}</Specification-Version>
    <Build-Number>${build.number}</Build-Number>
    <Build-Id>${build.id}</Build-Id>
    <Svn-Revison>${svn.revision}</Svn-Revison>
</manifestEntries>

That's in the maven-war-plugin archive configuration. But you can find it in other plugins too. Then in Hudson, part of the maven build invocation is:

-Dbuild.number=${BUILD_NUMBER}
-Dsvn.revision=${SVN_REVISION}
-Dbuild.id=${BUILD_ID}

which sets those defines that then maven picks up. And then its just a matter of looking in the MANIFEST.MF file that has been deployed on the server to see what version it is.

There's a git plugin, that offers a similar set of environment variables including:

  • GIT_COMMIT - SHA of the current
  • GIT_BRANCH - Name of the remote repository (defaults to origin), followed by name of the branch currently being used, e.g. "origin/master" or "origin/foo"

The combination of these two practices allows you to easily identify the build (because build numbers go forward, and have meaning, unlike sha checksums) and the specific git commit that it is built from.

Related Topic