AWS DevOps & Developer Productivity Blog
Implementing GitFlow Using AWS CodePipeline, AWS CodeCommit, AWS CodeBuild, and AWS CodeDeploy
February 9, 2024: Before reading this post, please note that AWS CodePipeline recently added support for Branch-based development and Monorepos simplifying the architecture discussed in this post.
This post provides a high-level framework for how to implement GitFlow using AWS CodePipeline, AWS CodeCommit, AWS CodeBuild, and AWS CodeDeploy. We also provide AWS CloudFormation templates and AWS CLI commands to help you get going.
Before we begin, we want to point out that we strongly encourage Amazon teams to practice continuous integration (CI) via “trunk-based development” where developers regularly merge their code changes into a central repository, ideally several times a day. When teams are able to regularly merge small changes they minimize the complexity of the merge and thereby the effort. Combining trunk-based continuous integration with continuous delivery (CI/CD) reduces the lead-time of getting a change into production.
As we’ll explain later, GitFlow involves creating multiple levels of branching off of master where changes to feature branches are only periodically merged all the way back to master to trigger a release. “Branch-based development” typically leads to fewer merges to master, higher levels of “merge debt,” and therefore longer lead time for getting a change to production.
Of course, not every team has achieved CI/CD nirvana. Many teams are still on their CI/CD journey or have situations that might make branching models like GitFlow attractive (or even mandatory). For this reason, we want to provide information that helps them use our tools to automate merge and release tasks. With that out of the way, let’s dive in!
When Linus Torvalds introduced Git version control in 2005, it really changed the way developers thought about branching and merging. Before Git, these tasks were scary and mostly avoided. As the tools became more mature, branching and merging became both cheap and simple. They are now part of the daily development workflow. In 2010, Vincent Driessen introduced GitFlow, which became an extremely popular branch and release management model. It introduced the concept of a develop branch as the mainline integration and the well-known master branch, which is always kept in a production-ready state. Both master and develop are permanent branches, but GitFlow also recommends short-lived feature, hotfix, and release branches, like so:
GitFlow guidelines:
- Use development as a continuous integration branch.
- Use feature branches to work on multiple features.
- Use release branches to work on a particular release (multiple features).
- Use hotfix branches off of master to push a hotfix.
- Merge to master after every release.
- Master contains production-ready code.
Now that you have some background, let’s take a look at how we can implement this model using services that are part of AWS Developer Tools: AWS CodePipeline, AWS CodeCommit, AWS CodeBuild, and AWS CodeDeploy. In this post, we assume you are familiar with these AWS services. If you aren’t, see the links in the Reference section before you begin. We also assume that you have installed and configured the AWS CLI.
Throughout the post, we use the popular GitFlow tool. It’s written on top of Git and automates the process of branch creation and merging. The tool follows the GitFlow branching model guidelines. You don’t have to use this tool. You can use Git commands instead.
For simplicity, production-like pipelines that have approval or testing stages have been omitted, but they can easily fit into this model. Also, in an ideal production scenario, you would keep Dev and Prod accounts separate.
AWS Developer Tools and GitFlow
Let’s take a look at how can we model AWS CodePipeline with GitFlow. The idea is to create a pipeline per branch. Each pipeline has a lifecycle that is tied to the branch. When a new, short-lived branch is created, we create the pipeline and required resources. After the short-lived branch is merged into develop, we clean up the pipeline and resources to avoid recurring costs.
The following would be permanent and would have same lifetime as the master and develop branches:
- AWS CodeCommit master/develop branch
- AWS CodeBuild project across all branches
- AWS CodeDeploy application across all branches
- AWS Cloudformation stack (EC2 instance) for master (prod) and develop (stage)
The following would be temporary and would have the same lifetime as the short-lived branches:
- AWS CodeCommit feature/hotfix/release branch
- AWS CodePipeline per branch
- AWS CodeDeploy deployment group per branch
- AWS Cloudformation stack (EC2 instance) per branch
Here’s how it would look:
Basic guidelines (assuming EC2/on-premises):
- Each branch has an AWS CodePipeline.
- AWS CodePipeline is configured with AWS CodeCommit as the source provider, AWS CodeBuild as the build provider, and AWS CodeDeploy as the deployment provider.
- AWS CodeBuild is configured with AWS CodePipeline as the source.
- Each AWS CodePipeline has an AWS CodeDeploy deployment group that uses the Name tag to deploy.
- A single Amazon S3 bucket is used as the artifact store, but you can choose to keep separate buckets based on repo.
Step 1: Use the following AWS CloudFormation templates to set up the required roles and environment for master and develop, including the commit repo, VPC, EC2 instance, CodeBuild, CodeDeploy, and CodePipeline.
$ aws cloudformation create-stack --stack-name GitFlowEnv \
--template-body https://s3.amazonaws.com/ee-assets-prod-us-east-1/modules/e3211b1daa9f4bea93f703e17ef0026f/v1/aws-devops-workshop-environment-setup.template \
--capabilities CAPABILITY_IAM
$ aws cloudformation create-stack --stack-name GitFlowCiCd \
--template-body https://s3.amazonaws.com/ee-assets-prod-us-east-1/modules/e3211b1daa9f4bea93f703e17ef0026f/v1/aws-pipeline-commit-build-deploy.template \
--capabilities CAPABILITY_IAM \
--parameters ParameterKey=MainBranchName,ParameterValue=master ParameterKey=DevBranchName,ParameterValue=develop
Here is how the pipelines should appear in the CodePipeline console:
Step 2: Push the contents to the AWS CodeCommit repo.
Download WebAppRepo.zip. Unzip the file, clone the repo, and then commit and push the contents to CodeCommit – WebAppRepo.
Step 3: Run git flow init in the repo to initialize the branches.
$ git flow init
Assume you need to start working on a new feature and create a branch.
$ git flow feature start <branch>
Step 4: Update the stack to create another pipeline for feature-x branch.
$ aws cloudformation update-stack --stack-name GitFlowCiCd \
--template-body https://s3.amazonaws.com/ee-assets-prod-us-east-1/modules/e3211b1daa9f4bea93f703e17ef0026f/v1/aws-pipeline-commit-build-deploy-update.template \
--capabilities CAPABILITY_IAM \
--parameters ParameterKey=MainBranchName,ParameterValue=master ParameterKey=DevBranchName,ParameterValue=develop ParameterKey=FeatureBranchName,ParameterValue=feature-x
When you’re done, you should see the feature-x branch in the CodePipeline console. It’s ready to build and deploy. To test, make a change to the branch and view the pipeline in action.
After you have confirmed the branch works as expected, use the finish command to merge changes into the develop branch.
$ git flow feature finish <feature>
After the changes are merged, update the AWS CloudFormation stack to remove the branch. This will help you avoid charges for resources you no longer need.
$ aws cloudformation update-stack --stack-name GitFlowCiCd \
--template-body https://s3.amazonaws.com/ee-assets-prod-us-east-1/modules/e3211b1daa9f4bea93f703e17ef0026f/v1/aws-pipeline-commit-build-deploy.template \
--capabilities CAPABILITY_IAM \
--parameters ParameterKey=MainBranchName,ParameterValue=master ParameterKey=DevBranchName,ParameterValue=develop
The steps for the release and hotfix branches are the same.
End result: Pipelines and deployment groups
You should end up with pipelines that look like this.
Next steps
If you take the CLI commands and wrap them in your own custom bash script, you can use GitFlow and the script to quickly set up and tear down pipelines and resources for short-lived branches. This helps you avoid being charged for resources you no longer need. Alternatively, you can write a scheduled Lambda function that, based on creation date, deletes the short-lived pipelines on a regular basis.
Summary
In this blog post, we showed how AWS CodePipeline, AWS CodeCommit, AWS CodeBuild, and AWS CodeDeploy can be used to model GitFlow. We hope you can use the information in this post to improve your CI/CD strategy, specifically to get your developers working in feature/release/hotfixes branches and to provide them with an environment where they can collaborate, test, and deploy changes quickly.
References
- AWS CodePipeline User Guide https://docs.thinkwithwp.com/codepipeline/latest/userguide/welcome.html
- AWS CodeCommit User Guide https://docs.thinkwithwp.com/codecommit/latest/userguide/welcome.html
- AWS CodeBuild User Guide https://docs.thinkwithwp.com/codebuild/latest/userguide/welcome.html
- AWS CodeDeploy User Guide https://docs.thinkwithwp.com/codedeploy/latest/userguide/welcome.html
- AWS DevOps Essentials https://github.com/awslabs/aws-devops-essential