Skip to content

Feature/deploy infrastructure (#5) #74

Feature/deploy infrastructure (#5)

Feature/deploy infrastructure (#5) #74

Workflow file for this run

name: Continuous Integration
on:
push:
branches:
- main
- 'feature/**'
paths:
- '.github/workflows/ci-pipeline.yaml'
- 'src/**/*'
- 'tests/**/*'
- 'terraform/**/*.tf'
- 'terraform/**/*.tfvars'
- 'Dockerfile'
- 'pyproject.toml'
env:
PYTHON_IMAGE: 'python:3.12-slim'
POETRY_VERSION: 2.1.2
DOCKER_BUILD_SUMMARY: false # Deactivate build summary generation
jobs:
check-infra-code:
name: Check Infrastructure Code
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./terraform
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Init Terraform CLI
uses: hashicorp/setup-terraform@v3
- name: Run Format Check
run: |
terraform fmt -check -diff
- name: Terraform Init
run: |
terraform init -input=false -backend=false
- name: Run Validation
run: |
terraform validate
test-and-check:
name: Test and Check Python Code
runs-on: ubuntu-latest
container: python:3.12-slim # TODO: How to use a variable here?
env:
POETRY_HOME: '/opt/poetry'
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Poetry
run: |
python -m venv $POETRY_HOME
$POETRY_HOME/bin/pip install --no-cache-dir poetry==$POETRY_VERSION
- name: Install Dependencies
run: |
cd $GITHUB_WORKSPACE
$POETRY_HOME/bin/poetry install --no-root --no-interaction --with=dev
- name: Run Pytest
run: |
$POETRY_HOME/bin/poetry run pytest tests/ -v
- name: Run Format Check
run: |
$POETRY_HOME/bin/poetry run black --check src/ tests/
- name: Run Linting
run: |
$POETRY_HOME/bin/poetry run mypy src/
# TODO (matrops): One could invest more effort here to generate SARIF files and integrate them in GitHub Security Monitoring
- name: Run SAST
run: |
$POETRY_HOME/bin/poetry run bandit src/ -r -ll # Report medium vulnerabilities or higher
scan-and-push:
name: Scan and Push Docker Resources
runs-on: ubuntu-latest
permissions:
# Needed for SARIF upload
security-events: write
actions: read
contents: read
env:
BUILD_IMAGE_NAME: tmp-image:latest
PUSH_IMAGE_NAME: python-cicd
TRIVY_REPORT_DIR: /tmp/trivy_reports
needs: test-and-check
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# I'm aware that Trivy has a GitHub Action, this is just for learning purposes
- name: Install Trivy
run: |
# According to https://trivy.dev/latest/getting-started/installation/#debianubuntu-official
sudo apt-get update
sudo apt-get install wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
# Recommended in https://github.com/docker/build-push-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Project Docker Image
uses: docker/build-push-action@v6
with:
load: true
tags: ${{ env.BUILD_IMAGE_NAME }}
- name: Setup Trivy Reports Directory
run: |
mkdir -p $TRIVY_REPORT_DIR
echo "Test" > $TRIVY_REPORT_DIR/debug.txt
- name: Run Trivy Image Scan
run: |
trivy image --severity HIGH,CRITICAL --format sarif -o trivy-image-report.sarif $BUILD_IMAGE_NAME
- name: Upload Trivy Image Scan Result
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-image-report.sarif
category: trivy-image-report
- name: Run Trivy Dockerfile Scan
run: |
trivy config --severity HIGH,CRITICAL --format sarif -o trivy-dockerfile-report.sarif .
- name: Upload Trivy Config Scan Result
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-dockerfile-report.sarif
category: trivy-dockerfile-report
# === Push Docker Image to registry ===
# My first approach was separating the Image Test and Push Stages, however this created too much hassle.
# Reason was that I wanted to avoid building the image twice. However, splitting the build and the push stages
# meant that I had to use artifacts to upload the built image in one stage and download & handle it in another
# I'm sure this would work eventually, however using the current approach was much faster and easier. Only
# downside is that image scanning and code testing is not parallelized anymore, resulting in a slightly
# slower CI/CD runtime.
- name: Log into Docker Hub
if: github.ref_name == 'main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get Image Version
if: github.ref_name == 'main'
id: get_image_version
run: |
IMAGE_VERSION=$(grep '^version = ' pyproject.toml | awk -F" " {'print $3'} | sed s/\"//g)
echo "Using image version '$IMAGE_VERSION'"
echo "image_version=$IMAGE_VERSION" >> $GITHUB_OUTPUT
- name: Push Docker Image
if: github.ref_name == 'main'
uses: docker/build-push-action@v6
with:
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PUSH_IMAGE_NAME }}:${{ steps.get_image_version.outputs.image_version }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PUSH_IMAGE_NAME }}:latest