name: CI/CD Pipeline - Northern Thailand Ping River Monitor on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: # Run tests daily at 2 AM UTC - cron: '0 2 * * *' env: PYTHON_VERSION: '3.11' REGISTRY: git.b4l.co.th IMAGE_NAME: b4l/northern-thailand-ping-river-monitor jobs: # Test job test: name: Test Suite runs-on: ubuntu-latest strategy: matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] 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 }}-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 -r requirements-dev.txt - name: Lint with flake8 run: | flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics flake8 src/ --count --exit-zero --max-complexity=10 --max-line-length=100 --statistics - name: Type check with mypy run: | mypy src/ --ignore-missing-imports - name: Format check with black run: | black --check src/ *.py - name: Import sort check run: | isort --check-only src/ *.py - name: Run integration tests run: | python tests/test_integration.py - name: Run station management tests run: | python tests/test_station_management.py - name: Test application startup run: | timeout 10s python run.py --test || true - name: Security scan with bandit run: | bandit -r src/ -f json -o bandit-report.json || true - name: Upload test artifacts uses: actions/upload-artifact@v3 if: always() with: name: test-results-${{ matrix.python-version }} path: | bandit-report.json *.log # Code quality job code-quality: name: Code Quality 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 -r requirements-dev.txt - name: Run safety check run: | safety check -r requirements.txt --json --output safety-report.json || true - name: Run bandit security scan run: | bandit -r src/ -f json -o bandit-report.json || true - name: Upload security reports uses: actions/upload-artifact@v3 with: name: security-reports path: | safety-report.json bandit-report.json # Build Docker image build: name: Build Docker Image runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITEA_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr type=sha,prefix={{branch}}- type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - name: Test Docker image run: | docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} python run.py --test # Integration test with services integration-test: name: Integration Test with Services runs-on: ubuntu-latest needs: build services: victoriametrics: image: victoriametrics/victoria-metrics:latest ports: - 8428:8428 options: >- --health-cmd "wget --quiet --tries=1 --spider http://localhost:8428/health" --health-interval 30s --health-timeout 10s --health-retries 3 steps: - name: Checkout code uses: actions/checkout@v4 - name: Wait for VictoriaMetrics run: | timeout 60s bash -c 'until curl -f http://localhost:8428/health; do sleep 2; done' - 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 -r requirements.txt - name: Test with VictoriaMetrics env: DB_TYPE: victoriametrics VM_HOST: localhost VM_PORT: 8428 run: | python run.py --test - name: Start API server env: DB_TYPE: victoriametrics VM_HOST: localhost VM_PORT: 8428 run: | python run.py --web-api & sleep 10 - name: Test API endpoints run: | curl -f http://localhost:8000/health curl -f http://localhost:8000/stations curl -f http://localhost:8000/metrics # Deploy to staging (only on develop branch) deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest needs: [test, build, integration-test] if: github.ref == 'refs/heads/develop' environment: name: staging url: https://staging.ping-river-monitor.b4l.co.th steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to staging run: | echo "Deploying to staging environment..." # Add your staging deployment commands here # Example: kubectl, docker-compose, or webhook call - name: Health check staging run: | sleep 30 curl -f https://staging.ping-river-monitor.b4l.co.th/health # Deploy to production (only on main branch, manual approval) deploy-production: name: Deploy to Production runs-on: ubuntu-latest needs: [test, build, integration-test] if: github.ref == 'refs/heads/main' environment: name: production url: https://ping-river-monitor.b4l.co.th steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to production run: | echo "Deploying to production environment..." # Add your production deployment commands here - name: Health check production run: | sleep 30 curl -f https://ping-river-monitor.b4l.co.th/health - name: Notify deployment run: | echo "✅ Production deployment successful!" echo "🌐 URL: https://ping-river-monitor.b4l.co.th" echo "📊 Grafana: https://grafana.ping-river-monitor.b4l.co.th" # Performance test (only on main branch) performance-test: name: Performance Test runs-on: ubuntu-latest needs: deploy-production if: github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Apache Bench run: | sudo apt-get update sudo apt-get install -y apache2-utils - name: Performance test API endpoints run: | # Test health endpoint ab -n 100 -c 10 https://ping-river-monitor.b4l.co.th/health # Test stations endpoint ab -n 50 -c 5 https://ping-river-monitor.b4l.co.th/stations # Test metrics endpoint ab -n 50 -c 5 https://ping-river-monitor.b4l.co.th/metrics # Cleanup old artifacts cleanup: name: Cleanup runs-on: ubuntu-latest if: always() needs: [test, build, integration-test] steps: - name: Clean up old Docker images run: | echo "Cleaning up old Docker images..." # Add cleanup commands for old images/artifacts