name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
name: Test Suite
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest-cov pytest-xdist
- name: Run tests with coverage
env:
INSTAGRAM_ACCESS_TOKEN: test_token_123
FACEBOOK_APP_ID: test_app_id_123
FACEBOOK_APP_SECRET: test_app_secret_123
INSTAGRAM_BUSINESS_ACCOUNT_ID: test_account_123
run: |
pytest tests/ -v --cov=src --cov-report=xml --cov-report=html --cov-report=term-missing
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
code-quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pylint black isort mypy bandit safety
- name: Run Black (code formatting)
run: black --check --diff src/ tests/
- name: Run isort (import sorting)
run: isort --check-only --diff src/ tests/
- name: Run Pylint
run: pylint src/ --rcfile=.pylintrc --output-format=github
- name: Run MyPy (type checking)
run: mypy src/ --config-file mypy.ini
- name: Run Bandit (security linting)
run: |
echo "🔒 Running Bandit security analysis..."
# Create empty JSON file first
echo '{"results": [], "metrics": {"_totals": {"confidence": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}, "severity": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}}}}' > bandit-report.json
# Run Bandit and capture exit code, but continue on error
bandit_exit_code=0
bandit -r src/ -f json -o bandit-report.json || bandit_exit_code=$?
echo "Bandit exit code: $bandit_exit_code"
echo "JSON file exists: $(test -f bandit-report.json && echo 'yes' || echo 'no')"
echo "JSON file size: $(wc -c < bandit-report.json 2>/dev/null || echo '0') bytes"
# Ensure file exists and has valid content
if [ ! -f bandit-report.json ] || [ ! -s bandit-report.json ]; then
echo "Creating fallback JSON file..."
echo '{"results": [], "metrics": {"_totals": {"confidence": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}, "severity": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}}}}' > bandit-report.json
fi
echo "Final Bandit report check:"
ls -la bandit-report.json
continue-on-error: true
- name: Run Safety (dependency security check)
run: |
echo "🛡️ Running Safety dependency check..."
# Create empty JSON file first
echo '[]' > safety-report.json
# Run Safety and capture exit code, but continue on error
safety_exit_code=0
safety check --json --output safety-report.json || safety_exit_code=$?
echo "Safety exit code: $safety_exit_code"
echo "JSON file exists: $(test -f safety-report.json && echo 'yes' || echo 'no')"
echo "JSON file size: $(wc -c < safety-report.json 2>/dev/null || echo '0') bytes"
# Ensure file exists and has valid content
if [ ! -f safety-report.json ] || [ ! -s safety-report.json ]; then
echo "Creating fallback JSON file..."
echo '[]' > safety-report.json
fi
echo "Final Safety report check:"
ls -la safety-report.json
continue-on-error: true
- name: Ensure security report files exist
run: |
echo "🔍 Final check - ensuring all security report files exist..."
# Guarantee bandit-report.json exists
if [ ! -f bandit-report.json ]; then
echo "⚠️ bandit-report.json missing - creating it"
echo '{"results": [], "metrics": {"_totals": {"confidence": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}, "severity": {"HIGH": 0, "LOW": 0, "MEDIUM": 0}}}}' > bandit-report.json
fi
# Guarantee safety-report.json exists
if [ ! -f safety-report.json ]; then
echo "⚠️ safety-report.json missing - creating it"
echo '[]' > safety-report.json
fi
# Verify files are readable and non-empty
echo "📁 Security report files status:"
ls -la bandit-report.json safety-report.json
echo "📊 File contents preview:"
echo "=== bandit-report.json ==="
head -c 200 bandit-report.json
echo ""
echo "=== safety-report.json ==="
head -c 200 safety-report.json
echo ""
if: always()
- name: Upload security reports
uses: actions/upload-artifact@v5
if: always()
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
docker:
name: Docker Build
runs-on: ubuntu-latest
needs: [test, code-quality]
steps:
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: instagram-mcp-server:latest
cache-from: type=gha
cache-to: type=gha,mode=max
release:
name: Release
runs-on: ubuntu-latest
needs: [test, code-quality, docker]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Create GitHub Release
uses: actions/create-release@v1
if: contains(github.event.head_commit.message, 'release:')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ github.run_number }}
release_name: Release v${{ github.run_number }}
body: |
Automated release from commit ${{ github.sha }}
Changes in this release:
${{ github.event.head_commit.message }}
draft: false
prerelease: false