14

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
3
  • Do you have a CI system such as Hudson or Jenkins? Is it capable of pushing tags of what it built back to git? (I know there are plugins for Hudson and Jenkins that can... - not as sure about others).
    – user40980
    Nov 6, 2015 at 22:03
  • @MichaelT We use MSBuild for our builds and Octopus Deploy for our deployments. I'm pretty confident we could make Octopus manipulate our git repository with a custom Powershell deployment script. Nov 6, 2015 at 22:11
  • We use Azure DevOps at my workplace, so to know what branch is in a given environment, I review the latest Deployment to that environment, which lists the Build it deployed, and the Build shows the branch that was used.
    – Graham
    May 12 at 20:23

4 Answers 4

16

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.

3

A completely different approach would be to dismiss the idea of versions completely. You only have "one version" which has a different configurable behaviour. The only difference would be, that you have a common codebase - even in production you would deploy work in progress: but not activated.

The difference only boils down to different sets of features being enabled in your product.

The de-/activation is done via feature toggles.

Upside: the whole release process is simplified: You are always delivering an integrated version of your software. Every feature is always available in master. No additional branches are needed.

There is no pain with merging features together, because: no branches no need for merging. There is no confusion what feature is on which branch and maybe depend or clashes with features from other branches. Every part could be activated at will. Even a rollback is not necessary anymore: it's only a flip of a switch.

I do not know, if that works for your codebase: the prerequisites in terms of code quality and developer discipline are quite high - you have to deal with "cleanup" after a feature becomes a core functionality and you have to manage a bunch of toggles preventing a bigger mess.

Perhaps it works for you.

2
  • But what about having different repository states deployed to different servers? Is the code that is running on the QA box the same as the one running on production to be able to reproduce a bug? Has the code that the developers pushed to their developer box the same as the code that QA is running against?
    – user40980
    Nov 7, 2015 at 13:13
  • 1
    The question has to be asked different: is the bug with a special configuration reproducible "now" if yes, you can fix it. If no - does the bug matter? You always push working (and integrated code). Nov 7, 2015 at 14:00
1

Hopefully you have some file or description field. If so you can use git-describe(1) in the filename or description. Then (with annotated tags) you will get:

  • just the tag name if it is on the tag; or
  • the tag name plus the abbreviated SHA1 (and how far it is from the tag).

So a reasonably human-readable name that you can use to trace back to the commit that the build came from.

-1

We use maven to put the version into the manifest file. Then have the application display the version if its a web app, or have a /version endpoint for web services.

Not the answer you're looking for? Browse other questions tagged or ask your own question.