name: CI/CD Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] release: types: [ published ] env: PYTHON_VERSION: "3.11" POETRY_VERSION: "1.7.1" jobs: code-quality: name: Code Quality Checks runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Run Ruff (Linting) run: ruff check src tests --output-format=github - name: Run Ruff (Formatting) run: ruff format src tests --check - name: Run MyPy (Type Checking) run: mypy src - name: Check imports with isort run: ruff check --select I src tests security-scan: name: Security Scanning runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" pip install safety bandit - name: Run Safety (Dependency vulnerability scan) run: safety check --json --output safety-report.json continue-on-error: true - name: Run Bandit (Security linting) run: bandit -r src/ -f json -o bandit-report.json continue-on-error: true - name: Upload Security Reports uses: actions/upload-artifact@v3 if: always() with: name: security-reports path: | safety-report.json bandit-report.json test: name: Tests runs-on: ubuntu-latest strategy: matrix: python-version: ["3.11", "3.12"] services: postgres: image: postgres:15 env: POSTGRES_PASSWORD: guardden_test POSTGRES_USER: guardden_test POSTGRES_DB: guardden_test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-${{ matrix.python-version }}-pip- - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Set up test environment env: GUARDDEN_DISCORD_TOKEN: "test_token_12345678901234567890123456789012345" GUARDDEN_DATABASE_URL: "postgresql://guardden_test:guardden_test@localhost:5432/guardden_test" GUARDDEN_AI_PROVIDER: "none" GUARDDEN_LOG_LEVEL: "DEBUG" run: | # Run database migrations for tests python -c " import os os.environ['GUARDDEN_DISCORD_TOKEN'] = 'test_token_12345678901234567890123456789012345' os.environ['GUARDDEN_DATABASE_URL'] = 'postgresql://guardden_test:guardden_test@localhost:5432/guardden_test' print('Test environment configured') " - name: Run tests with coverage env: GUARDDEN_DISCORD_TOKEN: "test_token_12345678901234567890123456789012345" GUARDDEN_DATABASE_URL: "postgresql://guardden_test:guardden_test@localhost:5432/guardden_test" GUARDDEN_AI_PROVIDER: "none" GUARDDEN_LOG_LEVEL: "DEBUG" run: | pytest --cov=src/guardden --cov-report=xml --cov-report=html --cov-report=term-missing - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 if: matrix.python-version == '3.11' with: file: ./coverage.xml flags: unittests name: codecov-umbrella fail_ci_if_error: false - name: Upload coverage reports uses: actions/upload-artifact@v3 if: matrix.python-version == '3.11' with: name: coverage-reports path: | coverage.xml htmlcov/ build-docker: name: Build Docker Image runs-on: ubuntu-latest needs: [code-quality, test] steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build Docker image uses: docker/build-push-action@v5 with: context: . push: false tags: guardden:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | INSTALL_AI=false - name: Build Docker image with AI uses: docker/build-push-action@v5 with: context: . push: false tags: guardden-ai:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | INSTALL_AI=true - name: Test Docker image run: | docker run --rm guardden:${{ github.sha }} python -m guardden --help deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest needs: [code-quality, test, build-docker] if: github.ref == 'refs/heads/develop' && github.event_name == 'push' environment: staging steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to staging run: | echo "Deploying to staging environment..." echo "This would typically involve:" echo "- Pushing Docker image to registry" echo "- Updating Kubernetes/Docker Compose configs" echo "- Running database migrations" echo "- Performing health checks" deploy-production: name: Deploy to Production runs-on: ubuntu-latest needs: [code-quality, test, build-docker] if: github.event_name == 'release' environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to production run: | echo "Deploying to production environment..." echo "This would typically involve:" echo "- Pushing Docker image to registry with version tag" echo "- Blue/green deployment or rolling update" echo "- Running database migrations" echo "- Performing comprehensive health checks" echo "- Monitoring deployment success" notification: name: Notification runs-on: ubuntu-latest needs: [code-quality, test, build-docker] if: always() steps: - name: Notify on failure if: contains(needs.*.result, 'failure') run: | echo "Pipeline failed. In a real environment, this would:" echo "- Send notifications to Discord/Slack" echo "- Create GitHub issue for investigation" echo "- Alert the development team" - name: Notify on success if: needs.code-quality.result == 'success' && needs.test.result == 'success' && needs.build-docker.result == 'success' run: | echo "Pipeline succeeded! In a real environment, this would:" echo "- Send success notification" echo "- Update deployment status" echo "- Trigger downstream processes"