Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 273 additions & 0 deletions .github/workflows/ci-cd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
name: CI/CD Pipeline

on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
schedule:
- cron: '0 2 * * *' # Daily at 2 AM UTC

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
lint-and-test:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Lint with pylint
run: |
pylint src/ --disable=R,C --fail-under=8.0 || true

- name: Format check with black
run: black --check src/ tests/ || true

- name: Type check with mypy
run: mypy src/ || true

- name: Run unit tests
run: |
pytest tests/unit/ -v --cov=src --cov-report=xml

- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage.xml

integration-tests:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_bq2pg
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Run integration tests
env:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: test_bq2pg
DB_USER: postgres
DB_PASSWORD: postgres
run: |
pytest tests/integration/ -v

security-scan:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'

- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Run bandit security check
run: |
pip install bandit
bandit -r src/ -f json -o bandit-report.json || true

build-and-push:
needs: [lint-and-test, integration-tests, security-scan]
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')

permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha

- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

deploy-staging:
needs: build-and-push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'

steps:
- uses: actions/checkout@v4

- name: Set up kubectl
uses: azure/setup-kubectl@v3

- name: Configure kubectl
run: |
echo "${{ secrets.KUBE_CONFIG_STAGING }}" | base64 -d > /tmp/kubeconfig
export KUBECONFIG=/tmp/kubeconfig

- name: Deploy with Helm
run: |
helm repo add bq2pg https://charts.example.com
helm repo update
helm upgrade --install bq2pg bq2pg/bq2pg \
--namespace bq2pg \
--values helm/bq2pg/values-staging.yaml \
--set image.tag=${{ github.sha }}

deploy-production:
needs: build-and-push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'

environment:
name: production
url: https://bq2pg.example.com

steps:
- uses: actions/checkout@v4

- name: Set up kubectl
uses: azure/setup-kubectl@v3

- name: Configure kubectl
run: |
echo "${{ secrets.KUBE_CONFIG_PROD }}" | base64 -d > /tmp/kubeconfig
export KUBECONFIG=/tmp/kubeconfig

- name: Deploy with Helm
run: |
helm repo add bq2pg https://charts.example.com
helm repo update
helm upgrade --install bq2pg bq2pg/bq2pg \
--namespace bq2pg \
--values helm/bq2pg/values-production.yaml \
--set image.tag=${{ github.sha }}

- name: Wait for deployment
run: |
kubectl -n bq2pg rollout status deployment/bq2pg-pipeline --timeout=10m

notify:
runs-on: ubuntu-latest
if: always()
needs: [lint-and-test, integration-tests, security-scan, build-and-push]

steps:
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "BQ2PG Pipeline Failed",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "[ERROR] *BQ2PG CI/CD Pipeline Failed*\n*Repo:* ${{ github.repository }}\n*Branch:* ${{ github.ref }}\n*Commit:* <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>"
}
}
]
}

- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "BQ2PG Pipeline Succeeded",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "[OK] *BQ2PG CI/CD Pipeline Succeeded*\n*Repo:* ${{ github.repository }}\n*Branch:* ${{ github.ref }}\n*Commit:* <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>"
}
}
]
}
Loading
Loading