Bamboo and Git-flow by Example-ish

Results of my testing with Bamboo and trying to find the best way to work with it using a git-flow branching model.

Background

Hot into testing with Jenkins and trying to get it to bend Git to my will, I got suspicious that there must be a better deployment tool than the various Jenkins plugins that get unwieldy quickly (especially when accounting for enterprise-level user management).  See my previous post for more on Jenkins and Git.  Anyhow, I took a look at what Bamboo was selling and decided to spin up a free trial.

I decided to drop all my preconceived (and misguided) notions of how I thought I might want build and deployment tools to work, and instead try to follow whatever Bamboo appears to be preaching as best practice.  I then tried to adapt that to the git-flow model… which turned out to not be as apparent as one might have assumed from the makers of SourceTree and its built-in git-flow feature.  It fact, git-flow isn’t really even mentioned in the Bamboo documentation and the examples are only loosely adaptable to it.  Futhermore, the deployment features of Bamboo are barely documented and not exampled at all in their quick-start guide.  Yet, in spite of these minor whines, Bamboo was mostly a pleasure to work with.  Jenkins wasn’t bad — it’s just that next to Bamboo it  doesn’t look so good.

Example-ish

Okay, I don’t feel like writing a whole dummies guide here so I’m just going to hit on whatever I feel has been left out of Atlassian’s documentation.  I will also show the creative workarounds I had to employ.  (Maybe I’ll write the dummy guide later if I find time.)

Git-flow versus Bamboo

There is one primary conflict between git-flow and Bamboo.  That is release versioning.  They both want to do it.  So who wins?  Who tells who what version we’re on… and how?

First off, I like that Bamboo has a nice versioning solution that is easy to use.  What I don’t like is that there is no built-in way to make it cooperate with your Git repository better (that I found).

What became apparent when stepping through the motions of a typical git-flow workflow, as a developer preparing for a product release, is that git-flow wins the release versioning war.  This is because git-flow expects you to set the release version long before Bamboo can get to it in its “Deployment Project” feature.  Typically, in Bamboo, you will not kick off any deployment projects until after you have merged your git-flow release branch into the master branch.  Yet, with git-flow, whatever you name your release branch will later automatically become your version tag when you merge to master.  Thus, git-flow wins.

Note 1: A Deployment Project in Bamboo is a different feature/object than what you may see in other deployment methodologies demonstrated in Bamboo’s documentation.  Those other deployments use Plan Stages/Jobs to execute the deployment tasks against the current artifact.  Deployment Projects execute similar tasks against artifacts specifically identified as releases.  This separation works really well.
Note 2: Bamboo docs show the possibility of Deployment Projects on Plan Branches (as opposed to your parent plan).  What they call Branch Deployments.  I would avoid this because you can’t then easily (if at all) restrict which branches get to deploy to which environments.  To me, this qualifies as unnecessary flexibility/complexity that should be removed before you hang yourself by it.  I suggest handling internal deployments to skunkworks testing environments through plan jobs rather than through Deployment Projects.

Git-flow Branching and Bamboo Branching

Assuming you already have a Git repo set up with both a master branch and a develop branch, link Bamboo to only your repo’s master branch.  You can link repos on the fly when setting up a new plan (details of which I am skipping), or you can link repos separately in Bamboo Admin as seen here:

Linked Repository

This is the one and only repo you should need to add to your plan to support the git-flow model.  The plan’s Repositories tab looks like this after to add a repo to your plan:

Plan Repository

Bamboo plans support branching just like Git repos do.  In fact, you can tell Bamboo to monitor your VCS repo for new branches and create a matching “plan branch” whenever one is found.

Additionally, chances are good that your git-flow repo already has a develop branch and other branches.  One way to catch up to those after setting up a plan the first time is to add them manually.  The Branches tab is where you manage automatic plan branch creation as well as add plan branches manually.

Branches Tab

The regex of “(feature|hotfix|release)/.+” seen above tells Bamboo to automatically create plan branches only for git-flow’s feature, hotfix, and release branches.

Selecting the “Create plan branch” button opens the following popup where you can select an existing repo branch and create a Bamboo plan branch to match:

Create Plan Branch popup

That’s pretty much it for all the straight-forward stuff of setting up git-flow branching with a Bamboo project plan.  Now, what do we do about release versioning and deployments?  Deployments are pretty straight forward as well.  It is with the versioning where we have to get a bit creative.

Git-flow Tagging and Bamboo Release Versioning

We already established above that git-flow wins the version management war, so now let’s bend Bamboo to its will.

Fortunately, new in version 5.7 of Bamboo, you can now load plan variables from files — which makes this task a bit easier than it would have been before (and, still, Bamboo could make this even easier if they just chose to automatically load any found Git tags into an array for us).

Therefore, the goal here is to save git-flow’s tag on the master branch into a file for later reading into a Bamboo plan variable.  This is really not as hard as it sounds.  Basically, check out the code using a Source Code Checkout task (I checkout to a subdirectory called “bundlespace” as you will see).  Then, run a script to grab the results of a git-tag command and write it to a file compatible with Bamboo’s variable injector (e.g. “version=1.1.0”).

This script (which assumes that the git-tag command will return only one tag in this situation — which is all that git-flow should produce) does what I just described and then also writes a VERSION file into the code tree and zips up the directory:

#!/bin/bash

# Inject the version into a temp file for Bamboo to load.
if test "${bamboo.planRepository.branchName}" == "master"
then
  # If this is the master branch, steal the version from the tag name.
  cd bundlespace
    # Git 1.7.9 or later is required for the "points-at" option.
    TAG_VER="$(git tag --points-at ${bamboo.planRepository.revision})"
  cd ..
  if test -z $TAG_VER
  then
    echo "Failed to find version tag or build version string!"
    exit 1
  fi
else
  # Make up version strings for all other branches.
  TAG_VER="CI.${bamboo.planRepository.branchName}.b${bamboo.buildNumber}"
  # Convert all "/" to "_".
  TAG_VER="${TAG_VER//\//_}"
fi
echo "version=$TAG_VER" > version.txt

# Inject the version number into a source code file.
echo "$TAG_VER" > bundlespace/VERSION

rm -f bat.zip
zip -rq bat bundlespace -x "**/.git**"

I’ll leave it to you to do something different with release tags if you want.

Update (22dec2014): I did some quick testing with Stash instead of Bitbucket and the TAG_VER=”$(git tag)” line broke.  The clones from the two are different even though I set the Bamboo settings the same for shallow cloning: Bitbucket clones include only the specific tags attached to the commit being built (conveniently); and the Stash clones include all tags in the repo (older tags and newer tags).  Instead of digging into the reasons why, I updated Git to a version that supports the “points-at” option and changed the above code.  The “contains” option will not work reliably if your clone includes all tags and you build older commits to master.  Using git-ls-remote is another possibility but updating Git was easier.

If for some reason you have more than one tag per commit on master (you shouldn’t by normal git-flow patterns) then here is one way to trim off tags (assuming the right tag can be found by such simple means — which is doubtful):

    # If more that one tag is returned, trim all but the last.
    #TAG_VER="${TAG_VER##*$'\n'}"
    # Or, maybe, trim all but the first tag.
    TAG_VER="${TAG_VER%%$'\n'*}"
    # (Warning: Git sorts tags alphanumerically, not by age.)
Side note: For the ultimate in speed and space savings, I would like to add a “git archive” command to this script as well (and skip the Source Code Checkout task).  To make git-archive work here, however, I would need to pass it a SHA1 commit hash (available in “${bamboo.planRepository.revision}”) but git-archive does not accept commit hashes as a tree-ish to remote repos.  Bugger.  If git-archive did work, it would make getting the tag a little harder with git-ls-remote, grep, and string trimming, but still doable.

Anyhow, for the voyeurs, my test project and Tasks tab for this step looks like this:

Package: Script Task

Side note: Why-oh-why Bamboo tries to handle both Windows and Unix type scripts in one task type without a definitive indicator is truly bizarre to me.  To get Bamboo to execute Linux scripts on only the Linux agents, I have to add a silly hack to each plan as well as each agent (how? is beyond the scope of this post).  Hopefully, our production install of Bamboo will be Unix flavors only and we can skip this hack but, well, we’ll see.

Now that we’ve written the version to a file, loading and using it works pretty well.  To load it, add an “Inject Bamboo variables” task to your task list after the script task that created the file.

Package: Inject Bamboo variables

To use the loaded variable, you need to first create a Deployment Project for your plan (see next section).  Once you have a Deployment Project, select the “Release versioning” button from the deployment configuration screen.

Release versioning button

Note that the variable name takes the form of bamboo.<namespace>.<varname> — which works out to “bamboo.inject.version” in my case (<varname> comes from the contents of the file: e.g. “version=1.1.0”).  Also, don’t let Bamboo one-up the last number for you.

Release versioning

Cheers!  Git-flow is now in control of release versioning in Bamboo.  Hopefully, Atlassian or some plugin writer will simplify this for us one day.

Bamboo Deployment Projects

There is really not much specific to git-flow that you need to know with regards to deployment projects (other than they work really well together).  Like the repo, just point the deployment project to the master branch and not one of the other branches.  It is not that you don’t need to deploy from these other branches (you do, I’m sure), it is that you don’t need a Bamboo “Deployment Project” for them.  Also, if you recall “Note 2” from above, I don’t recommend the use of “Deployment Branches” for handling deployments from various plan branches.  It has potential to get messy with the current lack of restrictions; whereas, plan stages, jobs, and tasks work just fine for this.  It may work for you, and that is fine, but why bother?

Linked deployment projects are indicated on the Stages tab.

Stages Tab - Deployments

Set the deployment project to release and deploy only from the master branch:

Edit Deployment Project

I set up the first environment with an automatic trigger to deploy; QA and Prod require a manual trigger (as hilited below).  There appears to be enough control here to do anything needed by enterprise shops but I haven’t yet dug too far into the permission features.

Deployment Environments

While triggers can be set to a specific branch, you still can override branches and releases in a manual deployment if you set up deployment branches.  Hence, why I now avoid them after testing them.  Perhaps you can lock the permissions down in a way to satisfy enterprise shops, but why risk it?  (You’ve been warned three times now.  Eh?)

Staging: Depoyment Trigger

Deployment project environment tasks are added and edited just like plan stage/job tasks (with some tasks restricted this way or that):

Staging: Deployment Tasks

Side note: Much like I had a hard time getting plan scripts to select the right kind of agent (Windows or Linux) I had to employ another silly hack here to get the deployment to select the right kind of agent (note the “dummy Linux selector” command in the above screenshot).  (I got this hack idea from Aiden Wong’s comment on this issue.)  This is a different kind of hack from what I did for job script tasks and has the advantage of being more visible as well as working with both job tasks and deployment tasks.  (Again, showing these hacks are beyond the scope of this post — follow the provided links to the Bamboo JIRA issues for more intel.)

That sums it up for deployment projects and how to use git-flow with Bamboo.  I recognize I skipped a lot of the hand holding of how to get from A to B, but that kind of step-by-step document would have required four times the number screenshots and would have been unreasonably long.  Bamboo’s manuals are fine in those regards, so read them.

Bamboo Plan Branch Cleanup

One thing I have not yet dug into deeply is branch cleanup (in both Git and Bamboo).  While it is apparent that Bamboo does a good job of detecting new branches in the Git repo and responding accordingly, I am not yet convinced Bamboo is responding well when branches are finished and deleted in the Git repo.  I would like to see Bamboo at least disable a plan branch when I finish and/or delete it through git-flow.  At most, I would like to see more control of automatic plan branch removal so that it is automatically removed if is both deleted from the repo and stale by X number of days.  What I get currently is an error from Bamboo that it can’t find the branch in the repo but no option for what to do next.

Bamboo can automatically remove stale plan branches but, by all appearances, this feature will also remove stale plan branches that are still active in the Git repo (I may be wrong here but such is the indication of the UI and docs).  You can easily protect your develop branch from this feature by marking it as “Do not clean up this plan branch automatically.”  You might, however, lose a stale feature branch before you intend.  But, again, I have not yet dug into this enough to be too critical.  Futhermore, I seem to be having some trouble getting BitBucket to respond to the git-flow features in SourceTree for finishing a branch like I would expect — which makes it harder to determine how Bamboo should be responding.  Therefore, considering that I have been self-learning Jenkins, Bamboo, and Git over the past few months, I may not be savvy enough judge these feature at this point in time.  It could be that manual cleanup in Bamboo proves to be the best practice no matter what features are found in Bamboo.  Hopefully, I will add more to this section later.

Git-flow Caveat

What the git-flow scripts actually do in the case of hotfixes does not match what was originally described in Driessen’s article on branching.  See git-flow issues #61 and #177 dating back to October 2010.  Apparently, there is not much desire to fix this.  Thus, when using git-flow, if you deploy a hotfix while a release branch is open, you will have to figure out how to merge the hotfix into the release yourself.

To that end, I created new git-flow diagrams to remind us of this caveat (both have been corrected in their own way… use whichever you prefer):

git-flow (updated)

git-flow-ish (flipped)

PDF’s found here:

A copy of the Lucidchart source for the latter diagram is found here:

(I have not bothered to publish the other one yet, if ever.)

Advertisements

13 thoughts on “Bamboo and Git-flow by Example-ish

  1. Hi Matt,

    I’m the Product Manager for Bamboo and I wanted to thank you for writing a detailed account of your experience setting Bamboo with Git. It’s great feedback for us and it gives some good insights on how we can improve build and deployment requirements.

    Thanks!

    Sten

    Like

  2. Hi Matt,

    I have couple of quick questions if you have time to answer them it would be greatly appreciated. Great Post btw.
    First question is about the regex, if I include feature in the regex won’t that go against GitFlow and auto merge feature branches into Master instead of develop branch, if I have branch merging enabled? I noticed you didn’t have branch merging enabled.

    Are you manually merging feature branches to develop branch and hotfix and release branches to master and develop branch?

    I guess a better question is are you doing any auto merging of branches in bamboo or auto downstream merging of branches in Stash? If you are, how are you using it with GitFlow workflow?

    Thanks,

    Chris

    Like

  3. Hi Chris,

    Thank you for the questions. This post was the result of a grand experiment in my final months of almost 6 years of contracting at Brown University — it was an effort to help them onto a new DevOps path before I left — an effort which seems to be going well. Unfortunately, however, that gig has ended and I don’t have the same playground I had. Also unfortunately, I am not a DevOps expert and cannot answer all questions with as much confidence as I would like.

    Regardless, thank you for the compliment. I wrote this fairly quickly and, while it could be more clear, it works for now.

    Regarding auto-merge features, I really didn’t see any use for them with the git-flow model (except, perhaps, in the case of merging release branch bugfixes back into develop). Maybe I’m missing something. If git-flow defines anything, it is that all merges to master are structured, and not automatic. Thus, I would say that just as git-flow wants to control the release versioning, git-flow also wants to control all merging. I see this kind of structure and discipline as a good thing.

    So, to say it differently, I ignored all merging features of Bamboo, Stash, and Bitbucket, and used only the git-flow merging features of SourceTree. The only time I had to deviate from git-flow merging was when I was trying to merge a hotfix into a release branch. SourceTree need not be used to execute the git-flow scripts, it was just convenient for me to use it.

    Like

  4. One thing I’d like to add is that if you have an auto-deploy project based around any branch that you didn’t setup in the Build or Deploy (so here master), the release versioning *does not* apply.

    This is actually something some people have asked for but Atlasssian hasn’t responded with an ETA. Currently, bamboo will simply release at ${branch-name}-{build-version} (with the actual bamboo variables, sorry I don’t have them on me).

    I’m not really sure why they did this without the ability to override. I understand they give it a naming convention but you can’t overwrite what they put.

    Just a heads up for anyone who may stumble upon this.

    https://jira.atlassian.com/browse/BAM-14422

    Like

  5. Hi.

    Looks like one of the “echos” in your script is missing the “e”. Also, the “test” command was not working for me, so I replaced it with a regular string comparison.

    Other than that, it worked perfectly. Great post.

    Thank you!

    Like

    1. What? I thought “cho” was that special command in *nix that magically makes everything work.

      Programmer 1542-92: i just found a three-line regex in the system that i can’t make sense of but it somehow just works

      Programmer 213AG.BQ: cho

      cho = Change Obviousness : Switches execution mode from logical to PFM. Not to be confused with “chop”.

      PFM = Pure Freakin Magic.

      chop = Change Operation : Toggles interpreter direction between forwards and backwards.

      😀

      Anyhow, thanks, Stanley, for pointing out the keyboard fumble. I’ll fix it. I suppose “echo” will work as well as “cho”. In regards to the tests, yeah, I’m not surprised you had to change them. I believe I chose the newest forms of “test” which also means they are the least compatible.

      Like

  6. Hey Matt,
    Thanks for this awesome article, really helped me a lot. One question on that topic:
    Why do you deploy your master-branch to Staging, QA and Production? Shouldn’t the staging and QA-step happen on the dev-branch and after completion be merged into master or am I getting something wrong here?

    Like

    1. All that depends on how you set up your shop and how careful you are with production deployments. Here, I’m a assuming a large shop with many servers for skunkworks, unit testing, alpha, beta, RCs… you name it. After all that, still, production-ready code must go through three more servers: staging — which is, basically, to make sure your deployment scripts work; QA — which is a fresh clone of prod to make sure it all plays well for final signoff by the stakeholders (and that you didn’t cheat the scripts that broke in staging — I’ve seen that before); and, at last, prod.

      Thus, non-production deployments should execute through Plan Stages and/or Jobs.

      Only production deployments should execute through Deployment Projects. Otherwise, it seems, a rogue or careless developer could put various environments at risk. If you are a fully-automated agile shop with only one production or production-like server then, yes, I suppose that is the only one that should be deployed to from your Deployment Project and master branch.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s