Gitflow and CI/CD at Pistil

2021, May 06    

A couple of weeks ago I joined Pistil Data, a cannabis data analytics startup. Coming into the engineering team, which is very much being formed, I noticed how every commit was being done directly to the master branch, and every deploy to dev, staging and prod, from the master branch as well. The team being small, this does make sense, but we’re looking into expanding it, and as we have more people working on the same codebase, we’ll need a better structure and flow around branches, pipelines and deployments, hence Gitflow.

Gitflow

You should read Atlassian’s post on gitflow (and the original post), but the basic idea is that a project using git should have the following branches:

  • master represents production at all times
  • release code coming from develop, about to be merged into master to be released to production
  • hotfix branched out off master, to fix production bugs. After it’s done and tested, it should be merged back into master so the fix goes out to production, and into develop so that the next release doesn’t lose it
  • develop contains all of master’s code, and it’s usually ahead. It represents code/features that are going out in the next release. Ideally, develop should represent your staging environment at all times, just like master represents production
  • feature-[\d+] feature branches, off develop. The number is a simple reference to your board system (Jira, GH issues, Zenhub, etc). Once the developer finishes working on the feature, they should open a pull request against develop

From the branches laid out above, we decided to drop release, since it adds even more overhead to the release cycle.

Azure DevOps

At Pistil, we use Azure DevOps to host our repositories, our Agile board, and as you’ll see below, our CI and CD pipelines.

It’s very similar to any of the other tools out there, like Github, Gitlab or Bitbucket.

The Plan

All that said, when we set out to implement gitflow and fix the CI/CD pipelines around it, this was the plan:

  • the master and develop branches should be locked for commits, only accepting pull requests
  • any commit to feature-[\d]+ branches should trigger a CI pipeline that runs unit tests, and blocks existing pull requests against develop if it fails
  • any merge into develop should:
    • trigger a CI pipeline to build the project’s artifact, and also runs the unit tests. If this pipeline fails, any pull request against master should be blocked
    • trigger a separate CI pipeline to run the project’s Integration Tests Our Integration Tests take too long to run, and currently we’re accepting the trade-off of having them in a separate pipeline that doesn’t block pull requests.
    • trigger a release CD pipeline to automatically deploy the build to the dev environment
    • trigger a separate release CD pipeline to create a release to staging. The actual deploy must still happen manually (sort of a business requirement for us at the moment, ideally it should deploy to staging automatically as well)
  • any merge into master should:
    • trigger a CI pipeline to build the project’s artifact
    • trigger a separate CI pipeline to run the project’s Integration Tests
    • trigger a release CD pipeline to deploy to prod manually

End result

All in all, the plan has been accomplished, except for the Integration Tests CI pipeline, which were set to run manually.

Below are our default branches develop and master. At the time of writing, we’ve merged and deleted every feature-[\d]+ branch into develop and then into master for the latest release, that’s why there’s no other branch:

Branches

Notice the blue badge, close to the develop branch, reading “Branch has policies”. It represents a policy that blocks every pull request against develop if the unit tests have failed. There’s no matching blue badge in the master branch because I still don’t have the necessary Azure DevOps permission to add one to a repository’s default branch, but I’ll add one soon.

Branch policy

Below, the relevant CI pipelines that run unit and integration tests, and build the project:

  • 2 for the develop branch: Integration Tests + (Build + Unit Tests)
  • 2 for the master branch: Integration Tests + Build
  • 1 for any feature-[\d]+ branch: Unit Tests

CI pipelines

The CD pipelines:

  • one that releases the develop branch automatically to the dev environment, and manually to the staging environment
  • another that releases master to the production environment, manually

CD pipelines

And finally, a quick look at how the develop CD release pipeline looks in Azure DevOps:

Develop branch CD pipeline

Next steps

A few things we have left to improve the process and pipelines:

  • Azure DevOps has something called Environments which could, I think, replace our current stages on the release pipelines, but from my readings, I couldn’t clearly understand the benefits of going with Environments, so more reading on that is necessary
  • A bit outside of AzureDevOps’ scope, but Azure App Services has a “Deployment” section with “Deployment Slots” and “Deployment Center” in there. It’s something worth exploring