Created by Shawn Mittal
Sr. Lead Data Scientist
Booz Allen Hamilton
What do we want our application to do?
Let's write an app that returns a string after visiting a url. We will use Flask, a Python framework for building lightweight websites.
Let's write the app. We've written a hello world function that returns a string of text.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Here is a simple web application!"
if __name__ == '__main__':
app.run()
Now we need to write a test for our application. Since it's not doing anything complex, we'll just check to see that the application is returning an http 200 response code.
import pytest
from app import app
@pytest.fixture
def flask_app():
yield app.app
@pytest.fixture
def client(flask_app):
return flask_app.test_client()
def test_index(flask_app, client):
res = client.get('/')
assert res.status_code == 200
While we don't have to, let's go ahead and containerize our web application. We'll write a Dockerfile to define what the container image should contain. We also need to make sure we expose the correct port so that the outside world can access our web application through the container.
FROM tiangolo/uwsgi-nginx:python3.8-alpine
# Set environment information
ENV LISTEN_PORT=5000
EXPOSE 5000
ENV UWSGI_INI uwsgi.ini
# Install python packages
WORKDIR /app
COPY requirements.txt .
RUN pip3 install --upgrade pip && \
pip3 install --no-cache-dir -r ./requirements.txt
# Add web application to image
COPY . .
The crux of CI/CD is automation. In order to automate the build, test, deploy process, GitHub provides a CI/CD capability called GitHub Actions that allows us to define steps in our pipeline via YAML.
name: CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# not using standard actions/python@v2. causes dependency problems with some packages
- name: Install Python and Dependencies
run: |
sudo apt-get update
sudo apt-get install python3.8 python3-pip
sudo rm /usr/bin/python
sudo ln -s /usr/bin/python3.8 /usr/bin/python
- name: Run Linter for Python Code
run: |
pip3 install flake8
sudo apt-get install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistic
- name: Build Docker Container
run: |
cd sample_webapp
docker build -t shawnmittal/sample-webapp:latest .
- name: Run Tests
run: docker run -t shawnmittal/sample-webapp:latest python3 -m pytest
Let's see the output of our automated static code analysis and tests.
We've deployed our webapp!