Welcome! This repo shows you how to set up a simple yet powerful CI/CD pipeline using GitHub Actions.
Every time you push to the main
branch, your Python Flask API app is automatically built, tested, and deployed to Google Cloud Run. 🎉
- ✅ Listens for pushes to the
main
branch - 🐳 Builds a Docker image of your Flask API app
- 🧪 Runs API tests using
unittest
to verify routes - 🚀 Deploys to Google Cloud Run if tests pass
- 🔒 Uses GitHub Secrets to keep credentials secure
Before you begin, ensure you have the following:
- ✅ A Google Cloud Project with Cloud Run enabled
- 🗂️ An Artifact Registry (Docker format)
- 👤 A GCP service account with:
- Cloud Run Admin
- Artifact Registry Writer
- IAM Token Creator permissions
- 🔐 These GitHub Secrets configured in your repository:
GCP_PROJECT_ID
– your GCP project IDGCP_REGION
– e.g.us-central1
GCP_SERVICE
– your Cloud Run service nameGCP_REGISTRY
– registry region, e.g.us-central1
GCP_CREDENTIALS_JSON
– service account key in JSON formatREGISTRY_NAME
- artifact registry name
- ✅ Checkout code from GitHub
- 🐍 Set up Python using
actions/setup-python
- 📦 Install dependencies via
pip install -r requirements.txt
- 🔐 Authenticate with GCP using the service account key
- 🔑 Docker login to Artifact Registry
- 🛠️ Build & Push Docker Image tagged with commit SHA
- 🧪 Run Unit Tests via Docker (
unittest
+ route test viacurl
) - 🚀 Deploy to Cloud Run (only if tests pass)
- 🌐 Output your Cloud Run URL so you can visit your deployed API
graph TD
A[Push to main branch] --> B[GitHub Actions Triggered]
B --> C[Checkout Repository]
C --> D[Set Up Python Environment]
D --> E[Install Dependencies]
E --> F[Authenticate with GCP]
F --> G[Docker Login to Artifact Registry]
G --> H[Build Docker Image]
H --> I[Push Image to Artifact Registry]
I --> J[Run Container Locally for Testing]
J --> K[Test Flask Route + Unit Tests]
K --> L{Tests Passed?}
L -- No --> M[Fail Job ❌]
L -- Yes --> N[Re-Authenticate to GCP]
N --> O[Deploy to Cloud Run]
O --> P[Output Cloud Run URL 🌐]
Here's the actual workflow file at .github/workflows/ci-cd.yaml
:
name: 'Build and Deploy to Cloud Run'
on:
push:
branches:
- main
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
REGION: ${{ secrets.GCP_REGION }}
SERVICE: ${{ secrets.GCP_SERVICE }}
REGION_REGISTORY: ${{ secrets.GCP_REGISTRY }}
CLOUD_RUN_SERVICE_ACCOUNT: ${{ secrets.CLOUD_RUN_SERVICE_ACCOUNT }}
REGISTRY: ${{ secrets.REGISTRY_NAME }}
jobs:
deploy:
runs-on: 'ubuntu-latest'
permissions:
contents: 'read'
id-token: 'write'
strategy:
matrix:
python-version: [3.9]
steps:
- name: 'Checkout'
uses: 'actions/checkout@v4'
- name: 'Set up Python'
uses: 'actions/setup-python@v2'
with:
python-version: ${{ matrix.python-version }}
- name: 'Install Python dependencies'
run: |
pip install -r requirements.txt
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS_JSON }}'
- name: 'Docker Auth'
uses: 'docker/login-action@v3'
with:
username: '_json_key'
password: '${{ secrets.GCP_CREDENTIALS_JSON }}'
registry: '${{ env.REGION_REGISTORY }}-docker.pkg.dev'
- name: 'Build and Push Container'
run: |-
gcloud auth configure-docker '${{ env.REGION_REGISTORY }}-docker.pkg.dev'
DOCKER_TAG="${{ env.REGION_REGISTORY }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{env.REGISTRY}}/test:${{ github.sha }}"
docker build --tag "${DOCKER_TAG}" .
docker push "${DOCKER_TAG}"
- name: 'Test Container'
run: |-
DOCKER_TAG="${{ env.REGION_REGISTORY }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{env.REGISTRY}}/test:${{ github.sha }}"
CONTAINER_ID=$(docker run -d -p 5000:5000 "$DOCKER_TAG")
sleep 10
python -m unittest test.py
TEST_STATUS=$?
docker stop "$CONTAINER_ID"
docker rm "$CONTAINER_ID"
exit $TEST_STATUS
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS_JSON }}'
token_format: 'access_token'
- name: 'Deploy to Cloud Run'
uses: 'google-github-actions/deploy-cloudrun@v2'
with:
service: '${{ env.SERVICE }}'
region: '${{ env.REGION }}'
image: '${{ env.REGION_REGISTORY }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REGISTRY }}/test:${{ github.sha }}'
- name: 'Show output'
run: echo ${{ steps.deploy.outputs.url }}