How to create a 3 Stage Deployment Pipeline With Github Actions
In this post I will show you how to setup a nice 3 stage deployment pipeline that will lint and test your code before deploying to a specific stage, using Github Actions
I will be simulating a Python application for this post, so you should modify the Python related stuff according to your project.
🔧 Build And Test Workflow
This workflow will lint, type check, and test the project before deploying to any stage. Create a .github/workflows/build-and-test.yml
file:
name: build-and-test
on: [push]
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
jobs:
lint-and-type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.ACCESS_TOKEN || github.token }}
- name: Install Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Dependencies
run: |
pip install black
pip install flake8
pip install mypy
- name: Lint
run: flake8 .
- name: Check Black
run: black --check .
- name: Type Checking
run: mypy .
build-and-test:
needs: lint-and-type-check
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: my-secret-pw
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
redis:
image: redis:4.0.10
ports:
- 6379:6379
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Install Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Dependencies
run: |
pip install -r requirements.txt
- name: Run Test Suite
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python -m pytest tests/
This workflow will trigger two jobs that will run sequentially. If the first one fails, the build-and-test
job will not run. You can make these jobs run in parallel by removing the needs: lint-and-type-check
clause.
For illustrative purposes I am making use of Github Actions's service containers feature which allows us to start different containers that our build can use. In this case MySQL and Redis.
When checking out the project repository, we specify the token with ${{ secrets.ACCESS_TOKEN || github.token }}
in order to allow Dependabot to run the CI job when the bot creates pull requests.
🚀 Deployment Workflows
We will be working with 3 stages: dev, staging, and production, which will be linked to a develop
, staging
, and master
branch respectively.
Therefore we will create 3 very similar deployment workflow files:
deploy-dev.yml
deploy-staging.yml
deploy-prod.yml
Here is an example of the develop deployment:
name: deploy-dev
on:
workflow_run:
workflows: ["build-and-test"]
branches: [develop]
types:
- completed
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
ref: develop
- name: Install Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Dependencies
run:
# Install any required dependencies here
- name: Deploy
# ... Run your own deployment process here
The key thing in this workflow is that it will only run if the previous build-and-test
workflow we created succeeds. Otherwise this workflow will not run. This happens thanks to the if
clause and the completed
type specified.
The workflows for the other environments should be the same. We simply need to change the branches for each stage accordingly.