mirror of
https://github.com/ruvnet/RuView.git
synced 2026-05-01 23:40:09 +00:00
GitHub Actions does not allow `secrets.X` to appear directly in
step-level `if:` expressions — only `env.X` is valid in that context.
Both ci.yml and security-scan.yml had Slack-notify steps gated on
`secrets.SLACK_WEBHOOK_URL != ''`, which made the entire workflow
fail to parse. Result: every push to main produced a 0-second failure
with 0 jobs run, masquerading as a CI signal that wasn't actually
running CI.
Confirmed root cause via:
gh api -X POST repos/.../actions/workflows/167079093/dispatches \
-f ref=main
→ 422 Invalid Argument - failed to parse workflow:
(Line: 315, Col: 11): Unrecognized named-value: 'secrets'
Fix: promote the secret to job-level `env:` so step-level `if:`
references `env.SLACK_WEBHOOK_URL`. The actual secret value still
flows through unchanged for the action's runtime use.
Same pattern applied to security-scan.yml line 406 (the existing
SECURITY_SLACK_WEBHOOK_URL gate).
After this lands, every push to main should produce real CI runs
that actually execute jobs and reflect repo health honestly. The
runs may still fail for *real* reasons (e.g., CI image dependencies,
test gaps), but they will fail visibly with logs instead of in 0s
with no jobs.
350 lines
No EOL
9.7 KiB
YAML
350 lines
No EOL
9.7 KiB
YAML
name: Continuous Integration
|
|
|
|
on:
|
|
push:
|
|
branches: [ main, develop, 'feature/*', 'feat/*', 'hotfix/*' ]
|
|
pull_request:
|
|
branches: [ main, develop ]
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
PYTHON_VERSION: '3.11'
|
|
NODE_VERSION: '18'
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository }}
|
|
|
|
jobs:
|
|
# Code Quality and Security Checks
|
|
code-quality:
|
|
name: Code Quality & Security
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install black flake8 mypy bandit safety
|
|
|
|
- name: Code formatting check (Black)
|
|
run: black --check --diff src/ tests/
|
|
|
|
- name: Linting (Flake8)
|
|
run: flake8 src/ tests/ --max-line-length=88 --extend-ignore=E203,W503
|
|
|
|
- name: Type checking (MyPy)
|
|
run: mypy src/ --ignore-missing-imports
|
|
|
|
- name: Security scan (Bandit)
|
|
run: bandit -r src/ -f json -o bandit-report.json
|
|
continue-on-error: true
|
|
|
|
- name: Dependency vulnerability scan (Safety)
|
|
run: safety check --json --output safety-report.json
|
|
continue-on-error: true
|
|
|
|
- name: Upload security reports
|
|
uses: actions/upload-artifact@v4
|
|
if: always()
|
|
with:
|
|
name: security-reports
|
|
path: |
|
|
bandit-report.json
|
|
safety-report.json
|
|
|
|
# Rust Workspace Tests
|
|
rust-tests:
|
|
name: Rust Workspace Tests
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Cache cargo
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
v2/target
|
|
key: ${{ runner.os }}-cargo-${{ hashFiles('v2/Cargo.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-cargo-
|
|
|
|
- name: Run Rust tests
|
|
working-directory: v2
|
|
run: cargo test --workspace --no-default-features
|
|
|
|
# Unit and Integration Tests
|
|
test:
|
|
name: Tests
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
python-version: ['3.10', '3.11', '3.12']
|
|
services:
|
|
postgres:
|
|
image: postgres:15
|
|
env:
|
|
POSTGRES_PASSWORD: postgres
|
|
POSTGRES_DB: test_wifi_densepose
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
ports:
|
|
- 5432:5432
|
|
|
|
redis:
|
|
image: redis:7
|
|
options: >-
|
|
--health-cmd "redis-cli ping"
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
ports:
|
|
- 6379:6379
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python ${{ matrix.python-version }}
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
cache: 'pip'
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install pytest-cov pytest-xdist
|
|
|
|
- name: Run unit tests
|
|
env:
|
|
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_wifi_densepose
|
|
REDIS_URL: redis://localhost:6379/0
|
|
ENVIRONMENT: test
|
|
run: |
|
|
pytest tests/unit/ -v --cov=src --cov-report=xml --cov-report=html --junitxml=junit.xml
|
|
|
|
- name: Run integration tests
|
|
env:
|
|
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_wifi_densepose
|
|
REDIS_URL: redis://localhost:6379/0
|
|
ENVIRONMENT: test
|
|
run: |
|
|
pytest tests/integration/ -v --junitxml=integration-junit.xml
|
|
|
|
- name: Upload coverage reports
|
|
uses: codecov/codecov-action@v4
|
|
with:
|
|
file: ./coverage.xml
|
|
flags: unittests
|
|
name: codecov-umbrella
|
|
|
|
- name: Upload test results
|
|
uses: actions/upload-artifact@v4
|
|
if: always()
|
|
with:
|
|
name: test-results-${{ matrix.python-version }}
|
|
path: |
|
|
junit.xml
|
|
integration-junit.xml
|
|
htmlcov/
|
|
|
|
# Performance and Load Tests
|
|
performance-test:
|
|
name: Performance Tests
|
|
runs-on: ubuntu-latest
|
|
needs: [test]
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install locust
|
|
|
|
- name: Start application
|
|
run: |
|
|
uvicorn src.api.main:app --host 0.0.0.0 --port 8000 &
|
|
sleep 10
|
|
|
|
- name: Run performance tests
|
|
run: |
|
|
locust -f tests/performance/locustfile.py --headless --users 50 --spawn-rate 5 --run-time 60s --host http://localhost:8000
|
|
|
|
- name: Upload performance results
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: performance-results
|
|
path: locust_report.html
|
|
|
|
# Docker Build and Test
|
|
docker-build:
|
|
name: Docker Build & Test
|
|
runs-on: ubuntu-latest
|
|
needs: [code-quality, test, rust-tests]
|
|
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.GITHUB_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: .
|
|
target: production
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
platforms: linux/amd64,linux/arm64
|
|
|
|
- name: Test Docker image
|
|
run: |
|
|
docker run --rm -d --name test-container -p 8000:8000 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
|
sleep 10
|
|
curl -f http://localhost:8000/health || exit 1
|
|
docker stop test-container
|
|
|
|
- name: Run container security scan
|
|
uses: aquasecurity/trivy-action@master
|
|
with:
|
|
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
|
format: 'sarif'
|
|
output: 'trivy-results.sarif'
|
|
|
|
- name: Upload Trivy scan results
|
|
uses: github/codeql-action/upload-sarif@v3
|
|
if: always()
|
|
with:
|
|
sarif_file: 'trivy-results.sarif'
|
|
|
|
# API Documentation
|
|
docs:
|
|
name: API Documentation
|
|
runs-on: ubuntu-latest
|
|
needs: [docker-build]
|
|
if: github.ref == 'refs/heads/main'
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
|
|
- name: Generate OpenAPI spec
|
|
run: |
|
|
python -c "
|
|
from src.api.main import app
|
|
import json
|
|
with open('openapi.json', 'w') as f:
|
|
json.dump(app.openapi(), f, indent=2)
|
|
"
|
|
|
|
- name: Deploy to GitHub Pages
|
|
uses: peaceiris/actions-gh-pages@v4
|
|
with:
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
publish_dir: ./docs
|
|
destination_dir: api-docs
|
|
|
|
# Notification
|
|
notify:
|
|
name: Notify
|
|
runs-on: ubuntu-latest
|
|
needs: [code-quality, test, rust-tests, performance-test, docker-build, docs]
|
|
if: always()
|
|
# GitHub Actions does not allow `secrets.X` directly in step-level `if:`
|
|
# expressions — only `env.X`. Promote the secret to env at job scope so
|
|
# the gating expression below is parseable.
|
|
env:
|
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
steps:
|
|
- name: Notify Slack on success
|
|
if: ${{ env.SLACK_WEBHOOK_URL != '' && needs.code-quality.result == 'success' && needs.test.result == 'success' && needs.docker-build.result == 'success' }}
|
|
uses: 8398a7/action-slack@v3
|
|
with:
|
|
status: success
|
|
channel: '#ci-cd'
|
|
text: '✅ CI pipeline completed successfully for ${{ github.ref }}'
|
|
|
|
- name: Notify Slack on failure
|
|
if: ${{ env.SLACK_WEBHOOK_URL != '' && (needs.code-quality.result == 'failure' || needs.test.result == 'failure' || needs.docker-build.result == 'failure') }}
|
|
uses: 8398a7/action-slack@v3
|
|
with:
|
|
status: failure
|
|
channel: '#ci-cd'
|
|
text: '❌ CI pipeline failed for ${{ github.ref }}'
|
|
|
|
- name: Create GitHub Release
|
|
if: github.ref == 'refs/heads/main' && needs.docker-build.result == 'success'
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: v${{ github.run_number }}
|
|
name: Release v${{ github.run_number }}
|
|
body: |
|
|
Automated release from CI pipeline
|
|
|
|
**Changes:**
|
|
${{ github.event.head_commit.message }}
|
|
|
|
**Docker Image:**
|
|
`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}`
|
|
draft: false
|
|
prerelease: false |