Files
ruvnet--RuView/.github/workflows/ci.yml
T
Dragan Spiridonov 36e70bf229 security: pin GitHub Actions to SHAs and bump vulnerable npm deps (#442)
* security: pin GitHub Actions to SHAs and bump vulnerable npm deps (#442)

Addresses confirmed findings from issue #442 (Pentesterra/DevGuard).

GitHub Actions — pin all third-party Action references in
security-scan.yml and ci.yml to verified commit SHAs (with the
matching version in a trailing comment for legibility):

  * snyk/actions/python              -> v1.0.0
  * aquasecurity/trivy-action        -> v0.36.0  (security-scan.yml + ci.yml)
  * bridgecrewio/checkov-action      -> v12.1347.0
  * tenable/terrascan-action         -> v1.4.1
  * checkmarx/kics-github-action     -> v2.1.20  (the action #442 named)
  * trufflesecurity/trufflehog       -> v3.95.2

  Verification:
    grep -rE 'uses:.*@(main|master|latest)$' .github/workflows/
  returns no matches.

npm deps in ui/mobile — add `overrides` forcing patched versions of
the three packages flagged by the DevGuard scanner, regenerate
package-lock.json:

  * @xmldom/xmldom@0.8.11  ->  0.8.13
  * node-forge@1.3.3       ->  ^1.4.0   (closes 3 HIGH advisories)
  * picomatch@2.3.1        ->  ^2.3.2   (transitive in jest tooling)

  npm audit totals: 25 -> 22 advisories (5 HIGH -> 2 HIGH).

Out of scope for this PR (tracked separately):
  * Sensing-server unauth REST API surface — opened as #443
    pending design-intent confirmation from @ruvnet.
  * Bearer-token-shaped string in git history — confirmed test
    seed per repo owner; no rotation required.

Refs: #442

Co-Authored-By: claude-flow <ruv@ruv.net>

* chore: add Dependabot config for github-actions and ui/mobile npm (#442)

Pairs with the SHA pinning from the previous commit so the pinned
versions get automated weekly bumps rather than drifting back to
mutable refs over time.

Scoped to the two ecosystems #442 surfaced findings in:
  * github-actions (root)  — the supply-chain risk
  * npm (ui/mobile)        — the @xmldom/xmldom, node-forge, picomatch
                             advisories

Other ecosystems (pip, cargo, desktop UI npm) deliberately omitted —
they can be added in a separate PR if desired.

Refs: #442

Co-Authored-By: claude-flow <ruv@ruv.net>

* chore(dependabot): expand to pip, cargo, and desktop UI npm (#442)

Broadens the Dependabot config from the initial 2 ecosystems
(github-actions + ui/mobile npm) to cover all 5 package surfaces
in the repo so pinned dependencies stay current across the board:

  + npm  /v2/crates/wifi-densepose-desktop/ui   (vite advisory live)
  + pip  /                                     (requirements.txt loose pins)
  + cargo /v2                                  (no cargo audit in CI yet)

Marginal cost is zero — Dependabot only opens PRs when an upstream
bump exists, and per-ecosystem pull-request limits cap the noise.
Each ecosystem labelled distinctly so PRs route cleanly.

Refs: #442

Co-Authored-By: claude-flow <ruv@ruv.net>

---------

Co-authored-by: claude-flow <ruv@ruv.net>
2026-04-28 08:46:51 -04:00

350 lines
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@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
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